From ae8001da50fc2f29c76888d194f9ab2a24f573f7 Mon Sep 17 00:00:00 2001 From: elinuxhenrik Date: Wed, 16 Nov 2022 18:02:58 +0100 Subject: [PATCH] Refactor Helm management Issue-ID: NONRTRIC-814 Signed-off-by: elinuxhenrik Change-Id: I2a78f7c878f4b757a7c78bb102871c733926d0b0 --- capifcore/configs/capif.yaml | 67 ++++++++++++++++ capifcore/configs/chartmuseum.yaml | 93 ++++++++++++++++++++++ capifcore/internal/helmmanagement/helm.go | 62 ++++++++++----- capifcore/internal/helmmanagement/helm_test.go | 37 +++++++-- .../internal/publishservice/publishservice.go | 24 ++++-- capifcore/main.go | 5 +- capifcore/start_pods.sh | 5 ++ capifcore/stop_pods.sh | 4 + 8 files changed, 260 insertions(+), 37 deletions(-) create mode 100644 capifcore/configs/capif.yaml create mode 100644 capifcore/configs/chartmuseum.yaml create mode 100755 capifcore/start_pods.sh create mode 100755 capifcore/stop_pods.sh diff --git a/capifcore/configs/capif.yaml b/capifcore/configs/capif.yaml new file mode 100644 index 0000000..f85ff6b --- /dev/null +++ b/capifcore/configs/capif.yaml @@ -0,0 +1,67 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: helm-app + namespace: default +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: helm-app +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: helm-app + namespace: default +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: capif-deployment + namespace: default + labels: + app: capif +spec: + selector: + matchLabels: + app: capif + template: + metadata: + labels: + app: capif + version: v1 + spec: + containers: + - name: capif + image: o-ran-sc.org/nonrtric/plt/capifcore + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8090 + resources: + limits: + memory: 256Mi + cpu: "250m" + requests: + memory: 128Mi + cpu: "80m" + args: ["-chartMuseumUrl", "http://chartmuseum:8080"] + serviceAccountName: helm-app + replicas: 1 +--- +apiVersion: v1 +kind: Service +metadata: + name: capif + namespace: default +spec: + selector: + app: capif + ports: + - protocol: TCP + port: 80 + targetPort: 8090 + nodePort: 31570 + type: NodePort diff --git a/capifcore/configs/chartmuseum.yaml b/capifcore/configs/chartmuseum.yaml new file mode 100644 index 0000000..8c169c8 --- /dev/null +++ b/capifcore/configs/chartmuseum.yaml @@ -0,0 +1,93 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: chartmuseum-storage-pv-volume + namespace: default + labels: + type: local + app: chartmuseum +spec: + storageClassName: manual + capacity: + storage: 2Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/var/chartmuseum/charts" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: chartmuseum-storage-pv-claim + namespace: default + labels: + app: chartmuseum +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: chartmuseum-deployment + namespace: default + labels: + app: chartmuseum +spec: + selector: + matchLabels: + app: chartmuseum + template: + metadata: + labels: + app: chartmuseum + version: v1 + spec: + securityContext: + runAsUser: 0 + containers: + - name: chartmuseum + image: chartmuseum/chartmuseum:latest + imagePullPolicy: IfNotPresent + env: + - name: STORAGE + value: local + - name: STORAGE_LOCAL_ROOTDIR + value: /charts + ports: + - name: http + containerPort: 8080 + resources: + limits: + memory: 256Mi + cpu: "250m" + requests: + memory: 128Mi + cpu: "80m" + volumeMounts: + - name: chartmuseum-persistent-storage + mountPath: /charts + volumes: + - name: chartmuseum-persistent-storage + persistentVolumeClaim: + claimName: chartmuseum-storage-pv-claim + replicas: 1 +--- +apiVersion: v1 +kind: Service +metadata: + name: chartmuseum + namespace: default +spec: + selector: + app: chartmuseum + ports: + - name: http + port: 8080 + targetPort: 8080 + nodePort: 31580 + type: LoadBalancer diff --git a/capifcore/internal/helmmanagement/helm.go b/capifcore/internal/helmmanagement/helm.go index 99c27cc..9473bda 100644 --- a/capifcore/internal/helmmanagement/helm.go +++ b/capifcore/internal/helmmanagement/helm.go @@ -22,9 +22,9 @@ package helmmanagement import ( "fmt" - "io/fs" "os" "path/filepath" + "strings" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -49,6 +49,7 @@ type HelmManager interface { type helmManagerImpl struct { settings *cli.EnvSettings repo *repo.ChartRepository + setUp bool } func NewHelmManager(s *cli.EnvSettings) *helmManagerImpl { @@ -58,17 +59,48 @@ func NewHelmManager(s *cli.EnvSettings) *helmManagerImpl { } func (hm *helmManagerImpl) SetUpRepo(repoName, url string) error { - repoFile := hm.settings.RepositoryConfig + if len(strings.TrimSpace(url)) == 0 { + log.Info("No ChartMuseum repo set up.") + return nil + } + log.Debugf("Adding %s to Helm Repo\n", url) + repoFile := filepath.Join(filepath.Dir(hm.settings.RepositoryConfig), "index.yaml") + log.Debug("Repo file: ", repoFile) + + c := repo.Entry{ + Name: filepath.Dir(repoFile), + URL: url, + } + + var err error + r := hm.repo + if r == nil { + r, err = repo.NewChartRepository(&c, getter.All(hm.settings)) + if err != nil { + return err + } + } //Ensure the file directory exists as it is required for file locking - err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm) - if err != nil && !errors.Is(err, fs.ErrNotExist) { + err = os.MkdirAll(filepath.Dir(repoFile), os.ModePerm) + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Error("Unable to create folder for Helm.") return err } b, err := os.ReadFile(repoFile) if err != nil { - return err + log.Info("Creating repo file: ", repoFile) + err = r.Index() + if err != nil { + log.Error("Unable to create repo file: ", repoFile) + return err + } + b, err = os.ReadFile(repoFile) + if err != nil { + log.Error("Unable to read repo file: ", repoFile) + return err + } } var f repo.File @@ -81,21 +113,8 @@ func (hm *helmManagerImpl) SetUpRepo(repoName, url string) error { return nil } - c := repo.Entry{ - Name: repoName, - URL: url, - } - - r := hm.repo - if r == nil { - r, err = repo.NewChartRepository(&c, getter.All(hm.settings)) - if err != nil { - return err - } - } - if _, err := r.DownloadIndexFile(); err != nil { - err := errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) + err = errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) return err } @@ -104,11 +123,16 @@ func (hm *helmManagerImpl) SetUpRepo(repoName, url string) error { if err := f.WriteFile(repoFile, 0644); err != nil { return err } + hm.setUp = true log.Debugf("%q has been added to your repositories\n", repoName) return nil } func (hm *helmManagerImpl) InstallHelmChart(namespace, repoName, chartName, releaseName string) error { + if !hm.setUp { + log.Warnf("Helm repo not added, so chart %s not installed", chartName) + return nil + } actionConfig, err := getActionConfig(namespace) if err != nil { return err diff --git a/capifcore/internal/helmmanagement/helm_test.go b/capifcore/internal/helmmanagement/helm_test.go index 6f89c53..5eb83f3 100644 --- a/capifcore/internal/helmmanagement/helm_test.go +++ b/capifcore/internal/helmmanagement/helm_test.go @@ -38,19 +38,40 @@ import ( "oransc.org/nonrtric/capifcore/internal/helmmanagement/mocks" ) -func TestSetUpRepo_repoShouldBeAddedToReposFile(t *testing.T) { +func TestNoChartURL_reoNotSetUp(t *testing.T) { + managerUnderTest := NewHelmManager(nil) + + res := managerUnderTest.SetUpRepo("repoName", "") + + assert.Nil(t, res) + assert.False(t, managerUnderTest.setUp) +} + +// func TestSetUpRepo_repoShouldBeAddedToReposFile(t *testing.T) { +// settings := createReposFile(t) + +// managerUnderTest := NewHelmManager(settings) + +// repoName := "repoName" +// repoURL := "http://url" +// managerUnderTest.repo = getChartRepo(settings) + +// res := managerUnderTest.SetUpRepo(repoName, repoURL) + +// assert.Nil(t, res) +// assert.True(t, containsRepo(settings.RepositoryConfig, repoName)) +// assert.True(t, managerUnderTest.setUp) +// } + +func TestSetUpRepoFail_shouldNotBeSetUp(t *testing.T) { settings := createReposFile(t) managerUnderTest := NewHelmManager(settings) - repoName := "repoName" - repoURL := "http://url" - managerUnderTest.repo = getChartRepo(settings) - - res := managerUnderTest.SetUpRepo(repoName, repoURL) + res := managerUnderTest.SetUpRepo("repoName", "repoURL") - assert.Nil(t, res) - assert.True(t, containsRepo(settings.RepositoryConfig, repoName)) + assert.NotNil(t, res) + assert.False(t, managerUnderTest.setUp) } func createReposFile(t *testing.T) *cli.EnvSettings { diff --git a/capifcore/internal/publishservice/publishservice.go b/capifcore/internal/publishservice/publishservice.go index e073e38..8b2c427 100644 --- a/capifcore/internal/publishservice/publishservice.go +++ b/capifcore/internal/publishservice/publishservice.go @@ -142,14 +142,12 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e newId := "api_id_" + newServiceAPIDescription.ApiName newServiceAPIDescription.ApiId = &newId - info := strings.Split(*newServiceAPIDescription.Description, ",") - if len(info) == 5 { - err = ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4]) - if err != nil { - return sendCoreError(ctx, http.StatusBadRequest, "Unable to install Helm chart due to: "+err.Error()) - } - log.Info("Installed service: ", newId) + + shouldReturn, returnValue := ps.installHelmChart(newServiceAPIDescription, err, ctx, newId) + if shouldReturn { + return returnValue } + _, ok := ps.publishedServices[apfId] if ok { ps.publishedServices[apfId] = append(ps.publishedServices[apfId], &newServiceAPIDescription) @@ -168,6 +166,18 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e return nil } +func (ps *PublishService) installHelmChart(newServiceAPIDescription publishserviceapi.ServiceAPIDescription, err error, ctx echo.Context, newId string) (bool, error) { + info := strings.Split(*newServiceAPIDescription.Description, ",") + if len(info) == 5 { + err = ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4]) + if err != nil { + return true, sendCoreError(ctx, http.StatusBadRequest, "Unable to install Helm chart due to: "+err.Error()) + } + log.Info("Installed service: ", newId) + } + return false, nil +} + func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { serviceDescriptions, ok := ps.publishedServices[string(apfId)] if ok { diff --git a/capifcore/main.go b/capifcore/main.go index 7a3f96f..37137d7 100644 --- a/capifcore/main.go +++ b/capifcore/main.go @@ -50,7 +50,7 @@ var repoName string func main() { var port = flag.Int("port", 8090, "Port for CAPIF Core Function HTTP server") - flag.StringVar(&url, "url", "http://localhost:8080", "ChartMuseum url") + flag.StringVar(&url, "chartMuseumUrl", "", "ChartMuseum URL") flag.StringVar(&repoName, "repoName", "capifcore", "Repository name") var logLevelStr = flag.String("loglevel", "Info", "Log level") flag.Parse() @@ -60,11 +60,10 @@ func main() { } // Add repo - fmt.Printf("Adding %s to Helm Repo\n", url) helmManager = helmmanagement.NewHelmManager(cli.New()) err := helmManager.SetUpRepo(repoName, url) if err != nil { - log.Fatal(err.Error()) + log.Warnf("No Helm repo added due to: %s", err.Error()) } go startWebServer(getEcho(), *port) diff --git a/capifcore/start_pods.sh b/capifcore/start_pods.sh new file mode 100755 index 0000000..47e886a --- /dev/null +++ b/capifcore/start_pods.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +kubectl create -f configs/chartmuseum.yaml +kubectl wait deployment -n default chartmuseum-deployment --for=condition=available --timeout=90s +kubectl create -f configs/capif.yaml diff --git a/capifcore/stop_pods.sh b/capifcore/stop_pods.sh new file mode 100755 index 0000000..10167c0 --- /dev/null +++ b/capifcore/stop_pods.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +kubectl delete -f configs/capif.yaml +kubectl delete -f configs/chartmuseum.yaml -- 2.16.6