2023-07-20 09:26:25 +00:00
|
|
|
package providers
|
|
|
|
|
|
|
|
import (
|
2023-09-22 11:02:56 +00:00
|
|
|
"errors"
|
2023-07-20 09:26:25 +00:00
|
|
|
"fmt"
|
2023-09-22 11:02:56 +00:00
|
|
|
"io"
|
2023-07-20 09:26:25 +00:00
|
|
|
"os"
|
2023-09-22 11:02:56 +00:00
|
|
|
"path/filepath"
|
2023-07-20 09:26:25 +00:00
|
|
|
|
2023-08-02 15:00:34 +00:00
|
|
|
"git.badhouseplants.net/allanger/shoebill/internal/config/release"
|
|
|
|
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
|
|
|
|
"git.badhouseplants.net/allanger/shoebill/internal/utils/diff"
|
|
|
|
"git.badhouseplants.net/allanger/shoebill/internal/utils/githelper"
|
2023-07-20 09:26:25 +00:00
|
|
|
release_v2beta1 "github.com/fluxcd/helm-controller/api/v2beta1"
|
|
|
|
helmrepo_v1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
|
2023-09-22 11:02:56 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2023-07-20 09:26:25 +00:00
|
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"sigs.k8s.io/yaml"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Flux struct {
|
|
|
|
path string
|
|
|
|
gh githelper.Githelper
|
|
|
|
}
|
|
|
|
|
|
|
|
func FluxProvider(path string, gh githelper.Githelper) Provider {
|
|
|
|
return &Flux{
|
|
|
|
path: path,
|
|
|
|
gh: gh,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *Flux) SyncState(diff diff.Diff) error {
|
|
|
|
entity := "repository"
|
|
|
|
srcPath := fmt.Sprintf("%s/src", f.path)
|
|
|
|
filePath := fmt.Sprintf("%s/%s-", srcPath, entity)
|
2023-09-22 11:02:56 +00:00
|
|
|
|
2023-07-20 09:26:25 +00:00
|
|
|
for _, repo := range diff.DeletedRepositories {
|
|
|
|
if err := os.Remove(filePath + repo.Name + ".yaml"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
message := `chore(repository): Removed a repo: %s
|
|
|
|
|
|
|
|
A repo has been removed from the cluster:
|
|
|
|
Name: %s
|
|
|
|
URL: %s
|
|
|
|
`
|
|
|
|
if err := f.gh.AddAllAndCommit(f.path, fmt.Sprintf(message, repo.Name, repo.Name, repo.URL)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-09-22 11:02:56 +00:00
|
|
|
|
2023-07-20 09:26:25 +00:00
|
|
|
for _, repo := range diff.UpdatedRepositories {
|
|
|
|
manifest, err := GenerateRepository(repo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := os.WriteFile(filePath+repo.Name+".yaml", manifest, os.ModeExclusive); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
message := `chore(repository): Update a repo: %s
|
|
|
|
|
|
|
|
A repo has been updated:
|
|
|
|
Name: %s
|
|
|
|
URL: %s
|
|
|
|
`
|
|
|
|
if err := f.gh.AddAllAndCommit(f.path, fmt.Sprintf(message, repo.Name, repo.Name, repo.URL)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, repo := range diff.AddedRepositories {
|
|
|
|
manifest, err := GenerateRepository(repo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
file, err := os.Create(filePath + repo.Name + ".yaml")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := file.Write(manifest); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
message := `chore(repository): Add a repo: %s
|
|
|
|
|
|
|
|
A new repo added to the cluster:
|
|
|
|
Name: %s
|
|
|
|
URL: %s
|
|
|
|
`
|
|
|
|
if err := f.gh.AddAllAndCommit(f.path, fmt.Sprintf(message, repo.Name, repo.Name, repo.URL)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2023-09-22 11:02:56 +00:00
|
|
|
|
2023-07-20 09:26:25 +00:00
|
|
|
entity = "release"
|
|
|
|
filePath = fmt.Sprintf("%s/%s-", srcPath, entity)
|
2023-09-22 11:02:56 +00:00
|
|
|
|
2023-07-20 09:26:25 +00:00
|
|
|
for _, release := range diff.AddedReleases {
|
2023-09-22 11:02:56 +00:00
|
|
|
if err := SyncValues(release, srcPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-07-20 09:26:25 +00:00
|
|
|
manifest, err := GenerateRelease(release)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
file, err := os.Create(filePath + release.Release + ".yaml")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
if _, err := file.Write(manifest); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
message := `chore(release): Add a new release: %s
|
|
|
|
|
|
|
|
A new release is added to the cluster:
|
|
|
|
Name: %s
|
|
|
|
Namespace: %s
|
|
|
|
Version: %s
|
|
|
|
Chart: %s/%s
|
|
|
|
`
|
|
|
|
if err := f.gh.AddAllAndCommit(f.path, fmt.Sprintf(message, release.Release, release.Release, release.Namespace, release.Version, release.Repository, release.Release)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2023-09-22 11:02:56 +00:00
|
|
|
|
2023-07-20 09:26:25 +00:00
|
|
|
for _, release := range diff.UpdatedReleases {
|
2023-09-22 11:02:56 +00:00
|
|
|
SyncValues(release, srcPath)
|
2023-07-20 09:26:25 +00:00
|
|
|
manifest, err := GenerateRelease(release)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.WriteFile(filePath+release.Release+".yaml", manifest, os.ModeExclusive); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := `chore(release): Update a release: %s
|
|
|
|
|
|
|
|
A release has been updated:
|
|
|
|
Name: %s
|
|
|
|
Namespace: %s
|
|
|
|
Version: %s
|
|
|
|
Chart: %s/%s
|
|
|
|
`
|
|
|
|
if err := f.gh.AddAllAndCommit(f.path, fmt.Sprintf(message, release.Release, release.Release, release.Namespace, release.Version, release.Repository, release.Release)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2023-09-22 11:02:56 +00:00
|
|
|
|
2023-07-20 09:26:25 +00:00
|
|
|
for _, release := range diff.DeletedReleases {
|
|
|
|
if err := os.Remove(filePath + release.Release + ".yaml"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
message := `chore(release): Remove a release: %s
|
|
|
|
|
|
|
|
A release has been removed from the cluster:
|
|
|
|
Name: %s
|
|
|
|
Namespace: %s
|
|
|
|
Version: %s
|
|
|
|
Chart: %s/%s
|
|
|
|
`
|
|
|
|
if err := f.gh.AddAllAndCommit(f.path, fmt.Sprintf(message, release.Release, release.Release, release.Namespace, release.Version, release.Repository, release.Release)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func GenerateRepository(repo *repository.Repository) ([]byte, error) {
|
|
|
|
fluxRepo := &helmrepo_v1beta2.HelmRepository{
|
|
|
|
TypeMeta: v1.TypeMeta{
|
|
|
|
Kind: helmrepo_v1beta2.HelmRepositoryKind,
|
|
|
|
APIVersion: helmrepo_v1beta2.GroupVersion.String(),
|
|
|
|
},
|
|
|
|
ObjectMeta: v1.ObjectMeta{
|
|
|
|
Name: repo.Name,
|
|
|
|
Namespace: "flux-namespace",
|
|
|
|
},
|
|
|
|
Spec: helmrepo_v1beta2.HelmRepositorySpec{
|
|
|
|
URL: repo.URL,
|
|
|
|
Type: repo.Kind,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return yaml.Marshal(&fluxRepo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateRelease and put
|
|
|
|
func GenerateRelease(release *release.Release) ([]byte, error) {
|
|
|
|
fluxRelease := &release_v2beta1.HelmRelease{
|
|
|
|
TypeMeta: v1.TypeMeta{
|
|
|
|
Kind: release_v2beta1.HelmReleaseKind,
|
|
|
|
APIVersion: release_v2beta1.GroupVersion.String(),
|
|
|
|
},
|
|
|
|
ObjectMeta: v1.ObjectMeta{
|
|
|
|
Name: release.Release,
|
|
|
|
Namespace: "flux-namespace",
|
|
|
|
},
|
|
|
|
Spec: release_v2beta1.HelmReleaseSpec{
|
|
|
|
Chart: release_v2beta1.HelmChartTemplate{
|
|
|
|
Spec: release_v2beta1.HelmChartTemplateSpec{
|
|
|
|
Chart: release.Chart,
|
|
|
|
Version: release.Version,
|
|
|
|
SourceRef: release_v2beta1.CrossNamespaceObjectReference{
|
|
|
|
Kind: helmrepo_v1beta2.HelmRepositoryKind,
|
|
|
|
Name: release.RepositoryObj.Name,
|
|
|
|
Namespace: "flux-namespace",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ReleaseName: release.Release,
|
|
|
|
Install: &release_v2beta1.Install{
|
|
|
|
CRDs: release_v2beta1.Create,
|
|
|
|
CreateNamespace: true,
|
|
|
|
},
|
|
|
|
TargetNamespace: "release-namespace",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return yaml.Marshal(&fluxRelease)
|
|
|
|
}
|
2023-09-22 11:02:56 +00:00
|
|
|
|
|
|
|
func SyncValues(release *release.Release, path string) error {
|
|
|
|
for _, valueFile := range release.Values {
|
|
|
|
// Prepare a dir for values
|
|
|
|
valuesPath := fmt.Sprintf("%s/%s", path, "values")
|
|
|
|
if _, err := os.Stat(valuesPath); errors.Is(err, os.ErrNotExist) {
|
|
|
|
err := os.Mkdir(valuesPath, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
destFileName := fmt.Sprintf("%s/%s-%s", valuesPath, release.Release, filepath.Base(valueFile))
|
|
|
|
var dstValues *os.File
|
|
|
|
var srcValues *os.File
|
|
|
|
var err error
|
|
|
|
srcValues, err = os.Open(valueFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer srcValues.Close()
|
|
|
|
if _, err = os.Stat(destFileName); err == nil {
|
|
|
|
dstValues, err = os.Open(destFileName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer dstValues.Close()
|
|
|
|
} else if errors.Is(err, os.ErrNotExist) {
|
|
|
|
dstValues, err = os.Create(destFileName)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
defer dstValues.Close()
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = io.Copy(dstValues, srcValues)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|