Refactor Helm management 96/9696/1
authorelinuxhenrik <henrik.b.andersson@est.tech>
Wed, 16 Nov 2022 17:02:58 +0000 (18:02 +0100)
committerelinuxhenrik <henrik.b.andersson@est.tech>
Thu, 17 Nov 2022 14:14:41 +0000 (15:14 +0100)
Issue-ID: NONRTRIC-814
Signed-off-by: elinuxhenrik <henrik.b.andersson@est.tech>
Change-Id: I2a78f7c878f4b757a7c78bb102871c733926d0b0

capifcore/configs/capif.yaml [new file with mode: 0644]
capifcore/configs/chartmuseum.yaml [new file with mode: 0644]
capifcore/internal/helmmanagement/helm.go
capifcore/internal/helmmanagement/helm_test.go
capifcore/internal/publishservice/publishservice.go
capifcore/main.go
capifcore/start_pods.sh [new file with mode: 0755]
capifcore/stop_pods.sh [new file with mode: 0755]

diff --git a/capifcore/configs/capif.yaml b/capifcore/configs/capif.yaml
new file mode 100644 (file)
index 0000000..f85ff6b
--- /dev/null
@@ -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 (file)
index 0000000..8c169c8
--- /dev/null
@@ -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
index 99c27cc..9473bda 100644 (file)
@@ -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
index 6f89c53..5eb83f3 100644 (file)
@@ -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 {
index e073e38..8b2c427 100644 (file)
@@ -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 {
index 7a3f96f..37137d7 100644 (file)
@@ -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 (executable)
index 0000000..47e886a
--- /dev/null
@@ -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 (executable)
index 0000000..10167c0
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+kubectl delete -f configs/capif.yaml
+kubectl delete -f configs/chartmuseum.yaml