WIP: Move public objects to the pkg dir

This commit is contained in:
Nikolai Rodionov
2023-10-12 11:53:58 +02:00
parent 6b4b170be1
commit 6a7e541b82
15 changed files with 24 additions and 25 deletions

View File

@ -1,89 +0,0 @@
package cluster
import (
"errors"
"fmt"
"os"
"git.badhouseplants.net/allanger/shoebill/internal/config/release"
"git.badhouseplants.net/allanger/shoebill/internal/lockfile"
"git.badhouseplants.net/allanger/shoebill/internal/utils/githelper"
)
type Cluster struct {
// Public
Name string
Git string
Releases []string
Provider string
DotSops string
// Internal
ReleasesObj release.Releases `yaml:"-"`
}
type Clusters []*Cluster
func (c *Cluster) CloneRepo(gh githelper.Githelper, workdir string, dry bool) error {
return gh.CloneRepo(workdir, c.Git, dry)
}
func (c *Cluster) BootstrapRepo(gh githelper.Githelper, workdir string, dry bool) error {
// - Create an empty lockfile
lockfilePath := fmt.Sprintf("%s/%s", workdir, lockfile.LOCKFILE_NAME)
if _, err := os.Stat(lockfilePath); errors.Is(err, os.ErrNotExist) {
file, err := os.Create(lockfilePath)
if err != nil {
return err
}
if _, err := file.WriteString("[]"); err != nil {
return err
}
srcDir := fmt.Sprintf("%s/src", workdir)
if err := os.MkdirAll(srcDir, 0777); err != nil {
return err
}
_, err = os.Create(fmt.Sprintf("%s/.gitkeep", srcDir))
if err != nil {
return err
}
if err := gh.AddAllAndCommit(workdir, "Bootstrap the shoebill repo"); err != nil {
return err
}
if !dry {
if err := gh.Push(workdir); err != nil {
return err
}
}
}
if len(c.DotSops) > 0 {
dotsopsPath := fmt.Sprintf("%s/.sops.yaml", workdir)
if _, err := os.Stat(dotsopsPath); errors.Is(err, os.ErrNotExist) {
file, err := os.Create(dotsopsPath)
if err != nil {
return err
}
if _, err := file.WriteString(c.DotSops); err != nil {
return err
}
if err := gh.AddAllAndCommit(workdir, "Create a sops config file"); err != nil {
return err
}
if !dry {
if err := gh.Push(workdir); err != nil {
return err
}
}
}
}
return nil
}
func (c *Cluster) PopulateReleases(releases release.Releases) {
c.ReleasesObj = releases
}
func (c *Cluster) CreateNewLockfile() error {
return nil
}

View File

@ -1 +0,0 @@
package cluster_test

View File

@ -1,34 +0,0 @@
package config
import (
"os"
"git.badhouseplants.net/allanger/shoebill/internal/config/cluster"
"git.badhouseplants.net/allanger/shoebill/internal/config/release"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
type Config struct {
Repositories repository.Repositories
Releases release.Releases
Clusters cluster.Clusters
ConfigPath string `yaml:"-"`
SopsBin string `yaml:"-"`
}
// NewConfigFromFile populates the config struct from a configuration yaml file
func NewConfigFromFile(path string) (*Config, error) {
var config Config
logrus.Infof("readig the config file: %s", path)
configFile, err := os.ReadFile(path)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(configFile, &config); err != nil {
return nil, err
}
config.ConfigPath = path
return &config, nil
}

View File

@ -1,53 +0,0 @@
package config_test
import (
"os"
"testing"
"git.badhouseplants.net/allanger/shoebill/internal/config"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"github.com/stretchr/testify/assert"
)
func helperCreateFile(t *testing.T) *os.File {
f, err := os.CreateTemp("", "sample")
if err != nil {
t.Error(err)
}
t.Logf("file is created: %s", f.Name())
return f
}
func helperFillFile(t *testing.T, f *os.File, content string) {
_, err := f.WriteString(content)
if err != nil {
t.Error(err)
}
}
func TestNewConfigFromFile(t *testing.T) {
f := helperCreateFile(t)
defer os.Remove(f.Name())
const configExample = `---
repositories:
- name: test
url: https://test.de
`
helperFillFile(t, f, configExample)
configGot, err := config.NewConfigFromFile(f.Name())
if err != nil {
t.Error(err)
}
repositoryWant := &repository.Repository{
Name: "test",
URL: "https://test.de",
}
configWant := &config.Config{
Repositories: repository.Repositories{repositoryWant},
}
assert.Equal(t, configWant.Repositories, configGot.Repositories)
}

View File

@ -1,190 +0,0 @@
package release
import (
"fmt"
"reflect"
"strings"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"git.badhouseplants.net/allanger/shoebill/internal/lockfile"
"git.badhouseplants.net/allanger/shoebill/internal/utils/helmhelper"
"git.badhouseplants.net/allanger/shoebill/internal/utils/sopshelper"
"github.com/sirupsen/logrus"
)
type Release struct {
// Public fields, that can be set with yaml
Repository string
// Release name
Release string `yaml:"name"`
// Chart name
Chart string
// Chart version
Version string
// Namespace to install release
Namespace string
// Value files
Values []string
// Secrets SOPS encrypted
Secrets []string
// Private fields that should be pupulated during the run-time
RepositoryObj *repository.Repository `yaml:"-"`
UnencryptedSecrets map[string][]byte `yaml:"-"`
}
type Releases []*Release
// Preare the release object
func (r *Release) InitRelease() {
r.UnencryptedSecrets = map[string][]byte{}
}
// RepositoryObjFromName gather the whole repository object by its name
func (r *Release) RepositoryObjFromName(repos repository.Repositories) error {
for _, repo := range repos {
if repo.Name == r.Repository {
r.RepositoryObj = repo
}
}
if r.RepositoryObj == nil {
return fmt.Errorf("couldn't gather the RepositoryObj for %s", r.Repository)
}
return nil
}
// Possible version placeholders
const (
VERSION_LATEST = "latest"
)
// Replace the version placeholder with the fixed version
func (r *Release) VersionHandler(dir string, hh helmhelper.Helmhelper) error {
switch r.Version {
case VERSION_LATEST:
version, err := hh.FindLatestVersion(dir, r.Chart, *r.RepositoryObj)
if err != nil {
return err
}
r.Version = version
}
return nil
}
func (r *Release) ValuesHandler(dir string) {
for i := range r.Values {
r.Values[i] = fmt.Sprintf("%s/%s", dir, strings.ReplaceAll(r.Values[i], "./", ""))
}
}
func (r *Release) SecretsHandler(dir string, sops sopshelper.SopsHelper) error {
for i := range r.Secrets {
path := fmt.Sprintf("%s/%s", dir, strings.ReplaceAll(r.Secrets[i], "./", ""))
res, err := sops.Decrypt(path)
if err != nil {
return err
}
r.UnencryptedSecrets[path] = res
}
return nil
}
func FindReleaseByNames(releases []string, releasesObj Releases) Releases {
result := Releases{}
for _, rObj := range releasesObj {
for _, r := range releases {
if rObj.Release == r {
result = append(result, rObj)
}
}
}
return result
}
// Helpers
func ReleasesFromLockfile(lockfile lockfile.LockFile, repos repository.Repositories) (Releases, error) {
releases := Releases{}
for _, releaseLocked := range lockfile {
release := &Release{
Repository: releaseLocked.RepoName,
Release: releaseLocked.Release,
Chart: releaseLocked.Chart,
Version: releaseLocked.Version,
Namespace: releaseLocked.Namespace,
RepositoryObj: &repository.Repository{
Name: releaseLocked.RepoName,
URL: releaseLocked.RepoUrl,
},
}
if err := release.RepositoryObj.ValidateURL(); err != nil {
return nil, err
}
if err := release.RepositoryObj.KindFromUrl(); err != nil {
return nil, err
}
releases = append(releases, release)
}
return releases, nil
}
func (r *Release) LockEntry() *lockfile.LockEntry {
return &lockfile.LockEntry{
Chart: r.Chart,
Release: r.Release,
Version: r.Version,
Namespace: r.Namespace,
RepoUrl: r.RepositoryObj.URL,
RepoName: r.RepositoryObj.Name,
}
}
type Diff struct {
Added Releases
Deleted Releases
Updated Releases
}
// TODO(@allanger): Naming should be better
func (src Releases) Diff(dest Releases) Diff {
diff := Diff{}
for _, rSrc := range src {
found := false
for _, rDest := range dest {
logrus.Infof("comparing %s to %s", rSrc.Release, rDest.Release)
if rSrc.Release == rDest.Release {
found = true
if reflect.DeepEqual(rSrc, rDest) {
continue
} else {
diff.Updated = append(diff.Updated, rDest)
}
}
}
if !found {
diff.Deleted = append(diff.Added, rSrc)
}
}
for _, rDest := range dest {
found := false
for _, rSrc := range src {
if rSrc.Release == rDest.Release {
found = true
continue
}
}
if !found {
diff.Added = append(diff.Added, rDest)
}
}
return diff
}
func (rs *Releases) PopulateRepositories(repos repository.Repositories) error {
for _, r := range *rs {
if err := r.RepositoryObjFromName(repos); err != nil {
return err
}
}
return nil
}

View File

@ -1,126 +0,0 @@
package release_test
import (
"fmt"
"testing"
"git.badhouseplants.net/allanger/shoebill/internal/config/release"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"git.badhouseplants.net/allanger/shoebill/internal/utils/helmhelper"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
)
func TestRepositoryObjFromNameExisting(t *testing.T) {
repos := []*repository.Repository{
{
Name: "test0",
URL: "https://test.test",
},
{
Name: "test1",
URL: "oco://test.test",
},
}
release := &release.Release{
Repository: "test0",
}
err := release.RepositoryObjFromName(repos)
if err != nil {
t.Error(err)
}
assert.Equal(
t,
release.RepositoryObj.Name,
"test0",
fmt.Sprintf("unexpected repo name: %s", release.RepositoryObj.Name),
)
assert.Equal(
t,
release.RepositoryObj.URL,
"https://test.test",
fmt.Sprintf("unexpected repo url: %s", release.RepositoryObj.URL),
)
}
func TestRepositoryObjFromNameNonExisting(t *testing.T) {
repos := []*repository.Repository{
{
Name: "test0",
URL: "https://test.test",
},
{
Name: "test1",
URL: "oco://test.test",
},
}
release := &release.Release{
Repository: "test_notfound",
}
err := release.RepositoryObjFromName(repos)
assert.ErrorContains(t, err,
"couldn't gather the RepositoryObj for test_notfound",
fmt.Sprintf("got an unexpected error: %s", err),
)
}
func TestRepositoryObjParsing(t *testing.T) {
t.Log("Repository Object should be empty after parsing")
rls := &release.Release{}
const yamlSnippet = `---
repository: test
repositoryObj:
name: test
url: test.test
`
if err := yaml.Unmarshal([]byte(yamlSnippet), &rls); err != nil {
t.Error(err)
}
assert.Equal(t, (*repository.Repository)(nil), rls.RepositoryObj, "release object should be empty")
}
func TestRepositoryObjFillingUp(t *testing.T) {
rls := &release.Release{
Repository: "test1",
}
expectedRepo := &repository.Repository{
Name: "test1",
URL: "oci://test.test",
Kind: repository.HELM_REPO_OCI,
}
var repos repository.Repositories = repository.Repositories{
&repository.Repository{
Name: "test1",
URL: "https://test.test",
Kind: repository.HELM_REPO_DEFAULT,
},
expectedRepo,
}
if err := rls.RepositoryObjFromName(repos); err != nil {
t.Error(err)
}
assert.Equal(t, expectedRepo, rls.RepositoryObj, "release object should be empty")
}
func TestVersionHandlerLatest(t *testing.T) {
hh := helmhelper.NewHelmMock()
rls := &release.Release{
Repository: "test1",
Version: "latest",
RepositoryObj: new(repository.Repository),
}
if err := rls.VersionHandler("", hh); err != nil {
t.Error(err)
}
assert.Equal(t, helmhelper.MOCK_LATEST_VERSION, rls.Version, "unexpected latest version")
}

View File

@ -1,64 +0,0 @@
package repository
import (
"fmt"
"regexp"
"strings"
)
/*
* Helm repo kinds: default/oci
*/
const (
HELM_REPO_OCI = "oci"
HELM_REPO_DEFAULT = "default"
)
type Repository struct {
Name string
URL string
Kind string `yaml:"-"`
}
type Repositories []*Repository
// ValidateURL returns error if the repo URL doens't follow the format
func (r *Repository) ValidateURL() error {
// An regex that should check if a string is a valid repo URL
const urlRegex = "^(http|https|oci):\\/\\/.*"
valid, err := regexp.MatchString(urlRegex, r.URL)
if err != nil {
return nil
}
if !valid {
return fmt.Errorf("it's not a valid repo URL: %s", r.URL)
}
return nil
}
// KindFromUrl sets Repository.Kind according to the prefix of an URL
func (r *Repository) KindFromUrl() error {
// It panics if URL is not valid,
// but invalid url should not pass the ValidateURL function
prefix := r.URL[:strings.IndexByte(r.URL, ':')]
switch prefix {
case "oci":
r.Kind = HELM_REPO_OCI
case "https", "http":
r.Kind = HELM_REPO_DEFAULT
default:
return fmt.Errorf("unknown repo kind: %s", prefix)
}
return nil
}
func (rs Repositories) NameByUrl(repoURL string) (string, error) {
for _, r := range rs {
if repoURL == r.URL {
return r.Name, nil
}
}
return "", fmt.Errorf("repo couldn't be found in the config: %s", repoURL)
}

View File

@ -1,107 +0,0 @@
package repository_test
import (
"fmt"
"testing"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"github.com/stretchr/testify/assert"
)
func TestValidateURLHttps(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "https://test.test",
}
err := repo.ValidateURL()
assert.NoError(t, err, fmt.Sprintf("unexpected err occured: %s", err))
}
func TestValidateURLOci(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "oci://test.test",
}
err := repo.ValidateURL()
assert.NoError(t, err, fmt.Sprintf("unexpected err occured: %s", err))
}
func TestValidateURLInvalid(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "invalid://test.test",
}
err := repo.ValidateURL()
assert.ErrorContains(t, err,
"it's not a valid repo URL: invalid://test.test",
fmt.Sprintf("got unexpected err: %s", err),
)
}
func TestValidateURLNonURL(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "test",
}
err := repo.ValidateURL()
assert.ErrorContains(t, err,
"it's not a valid repo URL: test",
fmt.Sprintf("got unexpected err: %s", err),
)
}
func TestKindFromUrlDefaultHttps(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "https://test.test",
}
if err := repo.KindFromUrl(); err != nil {
t.Error(err)
}
assert.Equal(t, repo.Kind,
repository.HELM_REPO_DEFAULT,
fmt.Sprintf("got unexpected repo type: %s", repo.Kind),
)
}
func TestKindFromUrlDefaultHttp(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "http://test.test",
}
if err := repo.KindFromUrl(); err != nil {
t.Error(err)
}
assert.Equal(t, repo.Kind,
repository.HELM_REPO_DEFAULT,
fmt.Sprintf("got unexpected repo type: %s", repo.Kind),
)
}
func TestKindFromUrlDefaultOci(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "oci://test.test",
}
if err := repo.KindFromUrl(); err != nil {
t.Error(err)
}
assert.Equal(t, repo.Kind,
repository.HELM_REPO_OCI,
fmt.Sprintf("got unexpected repo type: %s", repo.Kind),
)
}
func TestKindFromUrlDefaultInvalid(t *testing.T) {
repo := &repository.Repository{
Name: "test",
URL: "invalid:url",
}
err := repo.KindFromUrl()
assert.ErrorContains(t, err,
"unknown repo kind: invalid",
fmt.Sprintf("got unexpected err: %s", err))
}

View File

@ -4,9 +4,6 @@ import (
"fmt"
"path/filepath"
"git.badhouseplants.net/allanger/shoebill/internal/config"
"git.badhouseplants.net/allanger/shoebill/internal/config/release"
"git.badhouseplants.net/allanger/shoebill/internal/lockfile"
"git.badhouseplants.net/allanger/shoebill/internal/providers"
"git.badhouseplants.net/allanger/shoebill/internal/utils/diff"
"git.badhouseplants.net/allanger/shoebill/internal/utils/githelper"
@ -14,6 +11,9 @@ import (
"git.badhouseplants.net/allanger/shoebill/internal/utils/kustomize"
"git.badhouseplants.net/allanger/shoebill/internal/utils/sopshelper"
"git.badhouseplants.net/allanger/shoebill/internal/utils/workdir"
"git.badhouseplants.net/allanger/shoebill/pkg/config"
"git.badhouseplants.net/allanger/shoebill/pkg/lockfile"
"git.badhouseplants.net/allanger/shoebill/pkg/release"
)
func ReadTheConfig(path string) (*config.Config, error) {

View File

@ -1,87 +0,0 @@
package lockfile
import (
"fmt"
"os"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
)
const LOCKFILE_NAME = "shoebill.lock.yaml"
type LockEntry struct {
Chart string
Release string
Version string
Namespace string
RepoUrl string
RepoName string
}
type LockRepository struct {
URL string
Name string
}
type LockFile []*LockEntry
func NewFromFile(dir string) (LockFile, error) {
var lockEntries LockFile
lockfilePath := fmt.Sprintf("%s/%s", dir, LOCKFILE_NAME)
logrus.Infof("reading the lockfile file: %s", lockfilePath)
lockFile, err := os.ReadFile(lockfilePath)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(lockFile, &lockEntries); err != nil {
return nil, err
}
return lockEntries, nil
}
func (lockfile LockFile) ReposFromLockfile() (repository.Repositories, error) {
reposEntries := []LockRepository{}
for _, lockentry := range lockfile {
newRepoEntry := &LockRepository{
URL: lockentry.RepoUrl,
Name: lockentry.RepoName,
}
reposEntries = append(reposEntries, *newRepoEntry)
}
allKeys := make(map[string]bool)
dedupedEntries := []LockRepository{}
for _, repo := range reposEntries {
if _, value := allKeys[repo.Name]; !value {
allKeys[repo.Name] = true
dedupedEntries = append(dedupedEntries, repo)
}
}
repos := repository.Repositories{}
for _, repoEntry := range dedupedEntries {
repo := &repository.Repository{
Name: repoEntry.Name,
URL: repoEntry.URL,
}
if err := repo.KindFromUrl(); err != nil {
return nil, err
}
repos = append(repos, repo)
}
return repos, nil
}
func (lf LockFile) WriteToFile(dir string) error {
lockfilePath := fmt.Sprintf("%s/%s", dir, LOCKFILE_NAME)
lockfileContent, err := yaml.Marshal(lf)
if err != nil {
return err
}
if err := os.WriteFile(lockfilePath, lockfileContent, os.ModeExclusive); err != nil {
return nil
}
return nil
}

View File

@ -8,13 +8,12 @@ import (
"os/exec"
"path/filepath"
"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"
"git.badhouseplants.net/allanger/shoebill/pkg/release"
"git.badhouseplants.net/allanger/shoebill/pkg/repository"
release_v2beta1 "github.com/fluxcd/helm-controller/api/v2beta1"
helmrepo_v1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"

View File

@ -3,9 +3,9 @@ package diff
import (
"reflect"
"git.badhouseplants.net/allanger/shoebill/internal/config/release"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"git.badhouseplants.net/allanger/shoebill/internal/lockfile"
"git.badhouseplants.net/allanger/shoebill/pkg/lockfile"
"git.badhouseplants.net/allanger/shoebill/pkg/release"
"git.badhouseplants.net/allanger/shoebill/pkg/repository"
"github.com/sirupsen/logrus"
)

View File

@ -4,7 +4,7 @@ import (
"fmt"
"os"
"git.badhouseplants.net/allanger/shoebill/internal/config/repository"
"git.badhouseplants.net/allanger/shoebill/pkg/repository"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
"helm.sh/helm/v3/pkg/action"

View File

@ -1,6 +1,6 @@
package helmhelper
import "git.badhouseplants.net/allanger/shoebill/internal/config/repository"
import "git.badhouseplants.net/allanger/shoebill/pkg/repository"
const MOCK_LATEST_VERSION = "v1.12.1"

View File

@ -1,6 +1,6 @@
package helmhelper
import "git.badhouseplants.net/allanger/shoebill/internal/config/repository"
import "git.badhouseplants.net/allanger/shoebill/pkg/repository"
type Helmhelper interface {
FindLatestVersion(dir, chart string, repository repository.Repository) (string, error)