From 981ecd1e33cf96bfa5dc8b58fe09714f63803e1d Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Mon, 30 Dec 2024 09:05:37 +0530 Subject: [PATCH 1/7] Removed epinio. --- CHANGELOG.md | 1 + assets/epinio/mongodb-sb.yaml | 111 ---------------------- assets/epinio/mysql-sb.yaml | 111 ---------------------- assets/epinio/postgresql-sb.yaml | 110 ---------------------- assets/epinio/redis-sb.yaml | 111 ---------------------- main.go | 152 ------------------------------- 6 files changed, 1 insertion(+), 595 deletions(-) delete mode 100644 assets/epinio/mongodb-sb.yaml delete mode 100644 assets/epinio/mysql-sb.yaml delete mode 100644 assets/epinio/postgresql-sb.yaml delete mode 100644 assets/epinio/redis-sb.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index e395d49..b01fd7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Removed resource constraints on backend and frontend. +- Epinio is no longer installed. ## [1.0.4] - 2024-12-19 diff --git a/assets/epinio/mongodb-sb.yaml b/assets/epinio/mongodb-sb.yaml deleted file mode 100644 index 7a5856c..0000000 --- a/assets/epinio/mongodb-sb.yaml +++ /dev/null @@ -1,111 +0,0 @@ -apiVersion: application.epinio.io/v1 -kind: Service -metadata: - name: "mongodb-sb" - namespace: epinio -spec: - chartVersion: "16.3.0" - chart: mongodb - appVersion: "8.0.3" - helmRepo: - name: bitnami - url: https://charts.bitnami.com/bitnami - name: "mongodb-sb" - values: |- - architecture: standalone - primary: - persistence: - size: 2Gi - extraDeploy: - - | - # Create a service account, role and binding to allow to list, get and - # delete PVCs. It should be used by the job below. - - # To ensure the resources are deleted, use this annotation: - # - # annotations: - # "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - - # https://helm.sh/docs/topics/charts_hooks/#hook-resources-are-not-managed-with-corresponding-releases - # https://helm.sh/docs/topics/charts_hooks/#hook-deletion-policies - - --- - apiVersion: v1 - kind: ServiceAccount - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - - --- - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - kind: Role - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - rules: - - apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - delete - - list - - --- - kind: RoleBinding - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-5" - subjects: - - kind: ServiceAccount - name: "pvc-deleter-{{ .Release.Name }}" - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: "pvc-deleter-{{ .Release.Name }}" - - --- - apiVersion: batch/v1 - kind: Job - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - annotations: - # This is what defines this resource as a hook. Without this line, the - # job is considered part of the release. - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-4" - "helm.sh/hook-delete-policy": hook-succeeded - spec: - template: - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - spec: - restartPolicy: Never - serviceAccountName: "pvc-deleter-{{ .Release.Name }}" - containers: - - name: post-install-job - image: "rancher/kubectl:v1.29.10" - command: ["kubectl", "delete", "pvc", "-n", "{{ .Release.Namespace }}", "-l", "app.kubernetes.io/instance={{ .Release.Name }}"] diff --git a/assets/epinio/mysql-sb.yaml b/assets/epinio/mysql-sb.yaml deleted file mode 100644 index e9b599a..0000000 --- a/assets/epinio/mysql-sb.yaml +++ /dev/null @@ -1,111 +0,0 @@ -apiVersion: application.epinio.io/v1 -kind: Service -metadata: - name: "mysql-sb" - namespace: epinio -spec: - chartVersion: "9.14.4" - chart: mysql - appVersion: "8.0.35" - name: "mysql-sb" - helmRepo: - name: bitnami - url: https://charts.bitnami.com/bitnami - values: |- - primary: - persistence: - size: 2Gi - architecture: standalone - extraDeploy: - - | - # Create a service account, role and binding to allow to list, get and - # delete PVCs. It should be used by the job below. - - # To ensure the resources are deleted, use this annotation: - # - # annotations: - # "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - - # https://helm.sh/docs/topics/charts_hooks/#hook-resources-are-not-managed-with-corresponding-releases - # https://helm.sh/docs/topics/charts_hooks/#hook-deletion-policies - - --- - apiVersion: v1 - kind: ServiceAccount - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - - --- - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - kind: Role - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - rules: - - apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - delete - - list - - --- - kind: RoleBinding - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-5" - subjects: - - kind: ServiceAccount - name: "pvc-deleter-{{ .Release.Name }}" - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: "pvc-deleter-{{ .Release.Name }}" - - --- - apiVersion: batch/v1 - kind: Job - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - annotations: - # This is what defines this resource as a hook. Without this line, the - # job is considered part of the release. - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-4" - "helm.sh/hook-delete-policy": hook-succeeded - spec: - template: - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - spec: - restartPolicy: Never - serviceAccountName: "pvc-deleter-{{ .Release.Name }}" - containers: - - name: post-install-job - image: "rancher/kubectl:v1.29.10" - command: ["kubectl", "delete", "pvc", "-n", "{{ .Release.Namespace }}", "-l", "app.kubernetes.io/instance={{ .Release.Name }}"] diff --git a/assets/epinio/postgresql-sb.yaml b/assets/epinio/postgresql-sb.yaml deleted file mode 100644 index bb296b2..0000000 --- a/assets/epinio/postgresql-sb.yaml +++ /dev/null @@ -1,110 +0,0 @@ -apiVersion: application.epinio.io/v1 -kind: Service -metadata: - name: "postgresql-sb" - namespace: epinio -spec: - chartVersion: "13.2.4" - chart: postgresql - appVersion: "16.1.0" - name: "postgresql-sb" - helmRepo: - name: bitnami - url: https://charts.bitnami.com/bitnami - values: |- - primary: - persistence: - size: 2Gi - extraDeploy: - - | - # Create a service account, role and binding to allow to list, get and - # delete PVCs. It should be used by the job below. - - # To ensure the resources are deleted, use this annotation: - # - # annotations: - # "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - - # https://helm.sh/docs/topics/charts_hooks/#hook-resources-are-not-managed-with-corresponding-releases - # https://helm.sh/docs/topics/charts_hooks/#hook-deletion-policies - - --- - apiVersion: v1 - kind: ServiceAccount - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - - --- - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - kind: Role - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - rules: - - apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - delete - - list - - --- - kind: RoleBinding - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-5" - subjects: - - kind: ServiceAccount - name: "pvc-deleter-{{ .Release.Name }}" - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: "pvc-deleter-{{ .Release.Name }}" - - --- - apiVersion: batch/v1 - kind: Job - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - annotations: - # This is what defines this resource as a hook. Without this line, the - # job is considered part of the release. - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-4" - "helm.sh/hook-delete-policy": hook-succeeded - spec: - template: - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - spec: - restartPolicy: Never - serviceAccountName: "pvc-deleter-{{ .Release.Name }}" - containers: - - name: post-install-job - image: "rancher/kubectl:v1.29.10" - command: ["kubectl", "delete", "pvc", "-n", "{{ .Release.Namespace }}", "-l", "app.kubernetes.io/instance={{ .Release.Name }}"] diff --git a/assets/epinio/redis-sb.yaml b/assets/epinio/redis-sb.yaml deleted file mode 100644 index 8f8612c..0000000 --- a/assets/epinio/redis-sb.yaml +++ /dev/null @@ -1,111 +0,0 @@ -apiVersion: application.epinio.io/v1 -kind: Service -metadata: - name: "redis-sb" - namespace: epinio -spec: - chartVersion: "18.4.0" - chart: redis - appVersion: "7.2.4" - helmRepo: - name: bitnami - url: https://charts.bitnami.com/bitnami - name: "redis-sb" - values: |- - architecture: standalone - master: - persistence: - size: 1Gi - extraDeploy: - - | - # Create a service account, role and binding to allow to list, get and - # delete PVCs. It should be used by the job below. - - # To ensure the resources are deleted, use this annotation: - # - # annotations: - # "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - - # https://helm.sh/docs/topics/charts_hooks/#hook-resources-are-not-managed-with-corresponding-releases - # https://helm.sh/docs/topics/charts_hooks/#hook-deletion-policies - - --- - apiVersion: v1 - kind: ServiceAccount - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - - --- - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - kind: Role - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-6" - rules: - - apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - delete - - list - - --- - kind: RoleBinding - apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-5" - subjects: - - kind: ServiceAccount - name: "pvc-deleter-{{ .Release.Name }}" - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: "pvc-deleter-{{ .Release.Name }}" - - --- - apiVersion: batch/v1 - kind: Job - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - annotations: - # This is what defines this resource as a hook. Without this line, the - # job is considered part of the release. - "helm.sh/hook": post-delete - "helm.sh/hook-weight": "-4" - "helm.sh/hook-delete-policy": hook-succeeded - spec: - template: - metadata: - name: "pvc-deleter-{{ .Release.Name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - spec: - restartPolicy: Never - serviceAccountName: "pvc-deleter-{{ .Release.Name }}" - containers: - - name: post-install-job - image: "rancher/kubectl:v1.29.10" - command: ["kubectl", "delete", "pvc", "-n", "{{ .Release.Namespace }}", "-l", "app.kubernetes.io/instance={{ .Release.Name }}"] diff --git a/main.go b/main.go index bf2790e..bfd15ca 100644 --- a/main.go +++ b/main.go @@ -50,9 +50,6 @@ const ( var tektonAssets embed.FS var logger *log.Logger -//go:embed assets/epinio/* -var epinioAssets embed.FS - //go:embed assets/dashboards/* var dashboardAssets embed.FS @@ -105,8 +102,6 @@ type Config struct { type BackendConfig struct { ServiceAccountToken string - EpinioUsername string - EpinioPassword string PostgresUsername string PostgresPassword string PostgresDatabase string @@ -682,92 +677,6 @@ func generatePassword() string { return string(b) } -func installEpinio(config *Config, backendConfig *BackendConfig) error { - if resourceExists("deployment", "epinio-server", "epinio") { - printStatus("Epinio already installed") - return nil - } - - printStatus("Installing Epinio...") - - // Add Epinio helm repo - if err := addHelmRepo("epinio", "https://epinio.github.io/helm-charts"); err != nil { - return err - } - - if err := runCommand("helm", "repo", "update"); err != nil { - printError(fmt.Sprintf("Failed to update helm repos: %v", err)) - return err - } - - // Determine domain - var domain string - if config.DomainName != "" { - domain = config.DomainName - } else { - ip, err := getNodeIP() - if err != nil { - return err - } - domain = fmt.Sprintf("%s.nip.io", ip) - } - - // Set Epinio credentials - backendConfig.EpinioUsername = "shapeblock" - backendConfig.EpinioPassword = generatePassword() - - printStatus(fmt.Sprintf("Generated Epinio password: %s", backendConfig.EpinioPassword)) - logMessage("INFO", fmt.Sprintf("Epinio credentials - username: %s, password: %s", - backendConfig.EpinioUsername, backendConfig.EpinioPassword)) - - // Create values.yaml - values := fmt.Sprintf(` -global: - domain: %s - tlsIssuer: letsencrypt-prod - tlsIssuerEmail: %s - dex: - enabled: false -ingress: - ingressClassName: nginx -api: - users: - - username: %s - password: %s - roles: ["admin"] -epinioUI: - enabled: false -`, domain, config.AdminEmail, backendConfig.EpinioUsername, backendConfig.EpinioPassword) - - // Write values to temporary file - tmpfile, err := os.CreateTemp("", "epinio-values-*.yaml") - if err != nil { - printError(fmt.Sprintf("Failed to create temp file: %v", err)) - return err - } - defer os.Remove(tmpfile.Name()) - - if _, err := tmpfile.WriteString(values); err != nil { - printError(fmt.Sprintf("Failed to write values: %v", err)) - return err - } - tmpfile.Close() - - // Install Epinio - if err := runCommand("helm", "upgrade", "--install", - "epinio", "epinio/epinio", - "--version", "1.11.1", - "--namespace", "epinio", - "--create-namespace", - "--values", tmpfile.Name(), - "--timeout", "600s"); err != nil { - printError(fmt.Sprintf("Failed to install Epinio: %v", err)) - return err - } - - return nil -} - func installPostgres(name, username, database string, backendConfig *BackendConfig) error { printStatus(fmt.Sprintf("Installing PostgreSQL instance %s...", name)) @@ -924,49 +833,6 @@ master: return nil } -func installEpinioResources() error { - printStatus("Installing Epinio resources...") - - resources := []string{ - "assets/epinio/mongodb-sb.yaml", - "assets/epinio/mysql-sb.yaml", - "assets/epinio/postgresql-sb.yaml", - "assets/epinio/redis-sb.yaml", - } - - for _, resource := range resources { - // Read embedded file - content, err := epinioAssets.ReadFile(resource) - if err != nil { - printError(fmt.Sprintf("Failed to read resource %s: %v", resource, err)) - return err - } - - // Create temporary file - tmpfile, err := os.CreateTemp("", "epinio-*.yaml") - if err != nil { - printError(fmt.Sprintf("Failed to create temp file: %v", err)) - return err - } - defer os.Remove(tmpfile.Name()) - - // Write content to temp file - if _, err := tmpfile.Write(content); err != nil { - printError(fmt.Sprintf("Failed to write to temp file: %v", err)) - return err - } - tmpfile.Close() - - // Apply the resource - if err := runCommand("kubectl", "apply", "-f", tmpfile.Name(), "--kubeconfig=/etc/rancher/k3s/k3s.yaml", "-n", "epinio"); err != nil { - printError(fmt.Sprintf("Failed to install Epinio resource %s: %v", resource, err)) - return err - } - } - - return nil -} - func install(config *Config, backendConfig *BackendConfig) error { // Check if Kubernetes is already installed printStatus("Checking if Kubernetes is already installed...") @@ -1056,10 +922,6 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to create service account: %v", err) } - if err := installEpinio(config, backendConfig); err != nil { - return fmt.Errorf("failed to install Epinio: %v", err) - } - // Install main PostgreSQL instance if err := installPostgres("db", "shapeblock", "shapeblock", backendConfig); err != nil { return fmt.Errorf("failed to install main PostgreSQL: %v", err) @@ -1079,10 +941,6 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to install Redis: %v", err) } - if err := installEpinioResources(); err != nil { - return fmt.Errorf("failed to install Epinio resources: %v", err) - } - if err := installBackend(config, backendConfig); err != nil { return fmt.Errorf("failed to install shapeblock backend: %v", err) } @@ -2126,14 +1984,6 @@ func bootstrapBackend(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to read kubeconfig: %v", err) } - // Prepare epinio URL and token - domain := config.DomainName - if domain == "" { - domain = fmt.Sprintf("%s.nip.io", ip) - } - epinioURL := fmt.Sprintf("https://epinio.%s", domain) - epinioToken := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", backendConfig.EpinioUsername, backendConfig.EpinioPassword))) - // Prepare request payload homeDir, err := os.UserHomeDir() if err != nil { @@ -2155,8 +2005,6 @@ func bootstrapBackend(config *Config, backendConfig *BackendConfig) error { "password": config.AdminPassword, "ip": ip, "kubeconfig": string(kubeconfig), - "epinio_url": epinioURL, - "epinio_token": epinioToken, "ssh_private_key": string(privateKey), "ssh_public_key": string(publicKey), "first_name": config.AdminFirstName, From b8ce946192c31e105edc04525024d4a9623c87c8 Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Mon, 30 Dec 2024 09:17:27 +0530 Subject: [PATCH 2/7] Install container registry. --- CHANGELOG.md | 4 +++ go.mod | 3 +- go.sum | 6 ++-- main.go | 97 +++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 94 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b01fd7d..283b955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +### Added + +- Install container registry + ### Changed - Fix cron job creation diff --git a/go.mod b/go.mod index 5a14463..013f803 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,11 @@ go 1.22.2 require ( github.com/manifoldco/promptui v0.9.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f ) require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect - golang.org/x/sys v0.27.0 // indirect + golang.org/x/sys v0.28.0 // indirect ) diff --git a/go.sum b/go.sum index 0d15554..142a6a1 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,10 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWs github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go index bfd15ca..32596db 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "embed" "github.com/manifoldco/promptui" + "golang.org/x/crypto/bcrypt" "golang.org/x/exp/rand" ) @@ -954,12 +955,13 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to install frontend: %v", err) } - if err := installPrometheusStack(config); err != nil { - return fmt.Errorf("failed to install Prometheus Stack: %v", err) + // Install Docker Registry + if err := installRegistry(config); err != nil { + return fmt.Errorf("failed to install registry: %v", err) } - if err := pullBuildpackImage(); err != nil { - return fmt.Errorf("failed to pull buildpack image: %v", err) + if err := installPrometheusStack(config); err != nil { + return fmt.Errorf("failed to install Prometheus Stack: %v", err) } if err := printInstructions(config); err != nil { @@ -2017,6 +2019,16 @@ func bootstrapBackend(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to marshal payload: %v", err) } + // Get domain or use IP if not set + domain := config.DomainName + if domain == "" { + ip, err := getNodeIP() + if err != nil { + return fmt.Errorf("failed to get node IP: %v", err) + } + domain = fmt.Sprintf("%s.nip.io", ip) + } + // Make the API call url := fmt.Sprintf("https://api.%s/api/auth/bootstrap/", domain) @@ -2794,15 +2806,6 @@ func dumpLogs() error { return nil } -func pullBuildpackImage() error { - printStatus("Pulling buildpack image...") - if err := runCommand("k3s", "ctr", "images", "pull", "docker.io/paketobuildpacks/builder-jammy-full:latest"); err != nil { - return fmt.Errorf("failed to pull buildpack image: %v", err) - } - printStatus("Buildpack image pulled successfully") - return nil -} - func installPrometheusStack(config *Config) error { printStatus("Installing Prometheus Stack...") @@ -3152,3 +3155,71 @@ data: return nil } + +func installRegistry(config *Config) error { + printStatus("Installing Docker Registry...") + + // Check if registry already exists + if resourceExists("deployment", "registry-docker-registry", "shapeblock") { + printStatus("Docker Registry already installed") + return nil + } + + // Generate registry password + password := generatePassword() + + // Generate bcrypt hash of the password using golang's bcrypt + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return fmt.Errorf("failed to generate password hash: %v", err) + } + encryptedPassword := string(hashedPassword) + + // Add helm repo + if err := addHelmRepo("twuni", "https://helm.twun.io"); err != nil { + return err + } + + if err := runCommand("helm", "repo", "update"); err != nil { + return fmt.Errorf("failed to update helm repos: %v", err) + } + + // Get domain or use IP if not set + domain := config.DomainName + if domain == "" { + ip, err := getNodeIP() + if err != nil { + return fmt.Errorf("failed to get node IP: %v", err) + } + domain = fmt.Sprintf("%s.nip.io", ip) + } + + // Install registry + registryDomain := "registry." + domain + args := []string{ + "install", "registry", "twuni/docker-registry", + "--version", "2.2.3", + "--namespace", "shapeblock", + "--set", "persistence.enabled=true", + "--set", "persistence.size=10Gi", + "--set", "ingress.enabled=true", + "--set", fmt.Sprintf("ingress.hosts[0]=%s", registryDomain), + "--set", fmt.Sprintf("ingress.tls[0].hosts[0]=%s", registryDomain), + "--set", "ingress.tls[0].secretName=registry-tls", + "--set", "ingress.annotations.cert-manager\\.io/cluster-issuer=letsencrypt-prod", + "--set", "ingress.annotations.nginx\\.ingress\\.kubernetes\\.io/proxy-body-size=0", + "--set", fmt.Sprintf("secrets.htpasswd=shapeblock:%s", encryptedPassword), + "--set", "updateStrategy.type=Recreate", + } + + if err := runCommand("helm", args...); err != nil { + return fmt.Errorf("failed to install registry: %v", err) + } + + printStatus(fmt.Sprintf("Docker Registry installed successfully at %s", registryDomain)) + printStatus("Registry credentials:") + printStatus(" Username: shapeblock") + printStatus(fmt.Sprintf(" Password: %s", password)) + + return nil +} From cf48555a7bb522f4f5c48e99a98f8c6ead2f1be5 Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Mon, 30 Dec 2024 10:51:12 +0530 Subject: [PATCH 3/7] Kpack cluster stores and cluster stacks. --- CHANGELOG.md | 2 + assets/kpack/cluster-stack.yaml | 10 +++ assets/kpack/go-clusterstore.yaml | 9 +++ assets/kpack/nginx-clusterstore.yaml | 8 +++ assets/kpack/node-clusterstore.yaml | 8 +++ assets/kpack/python-clusterstore.yaml | 9 +++ assets/kpack/ruby-clusterstore.yaml | 9 +++ assets/kpack/sb-php-clusterstore.yaml | 19 ++++++ main.go | 93 +++++++++++++++++++++++++++ 9 files changed, 167 insertions(+) create mode 100644 assets/kpack/cluster-stack.yaml create mode 100644 assets/kpack/go-clusterstore.yaml create mode 100644 assets/kpack/nginx-clusterstore.yaml create mode 100644 assets/kpack/node-clusterstore.yaml create mode 100644 assets/kpack/python-clusterstore.yaml create mode 100644 assets/kpack/ruby-clusterstore.yaml create mode 100644 assets/kpack/sb-php-clusterstore.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 283b955..cd3386e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Install container registry +- Install kpack +- Install kpack cluster stores and cluster stacks ### Changed diff --git a/assets/kpack/cluster-stack.yaml b/assets/kpack/cluster-stack.yaml new file mode 100644 index 0000000..fdf31e4 --- /dev/null +++ b/assets/kpack/cluster-stack.yaml @@ -0,0 +1,10 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStack +metadata: + name: base +spec: + id: "io.buildpacks.stacks.jammy" + buildImage: + image: "paketobuildpacks/build-jammy-full" + runImage: + image: "paketobuildpacks/run-jammy-full" diff --git a/assets/kpack/go-clusterstore.yaml b/assets/kpack/go-clusterstore.yaml new file mode 100644 index 0000000..d308438 --- /dev/null +++ b/assets/kpack/go-clusterstore.yaml @@ -0,0 +1,9 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStore +metadata: + name: paketo-go +spec: + sources: + - image: index.docker.io/paketobuildpacks/go + - image: index.docker.io/paketobuildpacks/nodejs + - image: index.docker.io/shapeblock/chmod:0.0.5 diff --git a/assets/kpack/nginx-clusterstore.yaml b/assets/kpack/nginx-clusterstore.yaml new file mode 100644 index 0000000..3777dd7 --- /dev/null +++ b/assets/kpack/nginx-clusterstore.yaml @@ -0,0 +1,8 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStore +metadata: + name: paketo-nginx +spec: + sources: + - image: index.docker.io/paketobuildpacks/web-servers + - image: index.docker.io/shapeblock/chmod:0.0.5 diff --git a/assets/kpack/node-clusterstore.yaml b/assets/kpack/node-clusterstore.yaml new file mode 100644 index 0000000..a23dc24 --- /dev/null +++ b/assets/kpack/node-clusterstore.yaml @@ -0,0 +1,8 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStore +metadata: + name: paketo-node +spec: + sources: + - image: index.docker.io/paketobuildpacks/nodejs + - image: index.docker.io/shapeblock/chmod:0.0.5 diff --git a/assets/kpack/python-clusterstore.yaml b/assets/kpack/python-clusterstore.yaml new file mode 100644 index 0000000..380c10b --- /dev/null +++ b/assets/kpack/python-clusterstore.yaml @@ -0,0 +1,9 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStore +metadata: + name: paketo-python +spec: + sources: + - image: index.docker.io/paketobuildpacks/python + - image: index.docker.io/paketobuildpacks/nodejs + - image: index.docker.io/shapeblock/chmod:0.0.5 diff --git a/assets/kpack/ruby-clusterstore.yaml b/assets/kpack/ruby-clusterstore.yaml new file mode 100644 index 0000000..d0e4019 --- /dev/null +++ b/assets/kpack/ruby-clusterstore.yaml @@ -0,0 +1,9 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStore +metadata: + name: paketo-ruby +spec: + sources: + - image: index.docker.io/paketobuildpacks/ruby + - image: index.docker.io/paketobuildpacks/nodejs + - image: index.docker.io/shapeblock/chmod:0.0.5 diff --git a/assets/kpack/sb-php-clusterstore.yaml b/assets/kpack/sb-php-clusterstore.yaml new file mode 100644 index 0000000..937a96b --- /dev/null +++ b/assets/kpack/sb-php-clusterstore.yaml @@ -0,0 +1,19 @@ +apiVersion: kpack.io/v1alpha2 +kind: ClusterStore +metadata: + name: sb-php +spec: + sources: + - image: index.docker.io/paketobuildpacks/ca-certificates + - image: index.docker.io/paketobuildpacks/php + - image: index.docker.io/paketobuildpacks/php-dist + - image: index.docker.io/paketobuildpacks/composer + - image: registry.gitlab.com/shapeblock-buildpacks/composer-install:0.0.1 + - image: index.docker.io/paketobuildpacks/nginx + - image: index.docker.io/paketobuildpacks/php-fpm + - image: registry.gitlab.com/shapeblock-buildpacks/php-nginx:0.0.1 + - image: index.docker.io/paketobuildpacks/php-start + - image: index.docker.io/paketobuildpacks/nodejs + - image: index.docker.io/paketobuildpacks/procfile + - image: index.docker.io/shapeblock/apt:0.3.1 + - image: index.docker.io/shapeblock/chmod:0.0.5 diff --git a/main.go b/main.go index 32596db..bfbdf38 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,9 @@ var logger *log.Logger //go:embed assets/dashboards/* var dashboardAssets embed.FS +//go:embed assets/kpack/* +var kpackAssets embed.FS + // Add this near the top of the file with other constants var githubToken string // Will be set during compilation @@ -960,6 +963,15 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to install registry: %v", err) } + // Install kpack + if err := installKpack(); err != nil { + return fmt.Errorf("failed to install kpack: %v", err) + } + + if err := installKpackResources(); err != nil { + return fmt.Errorf("failed to install kpack resources: %v", err) + } + if err := installPrometheusStack(config); err != nil { return fmt.Errorf("failed to install Prometheus Stack: %v", err) } @@ -3223,3 +3235,84 @@ func installRegistry(config *Config) error { return nil } + +func installKpack() error { + printStatus("Installing kpack...") + + // Check if kpack already exists + if resourceExists("deployment", "kpack-controller", "kpack") { + printStatus("kpack already installed") + return nil + } + + // Create kpack namespace + if err := runCommand("kubectl", "create", "namespace", "kpack"); err != nil { + if !strings.Contains(err.Error(), "already exists") { + return fmt.Errorf("failed to create kpack namespace: %v", err) + } + } + + // Add shapeblock helm repo + if err := addHelmRepo("shapeblock", "https://shapeblock.github.io"); err != nil { + return err + } + + // Install kpack using helm + if err := runCommand("helm", "install", "kpack", + "shapeblock/sb-kpack", + "--version", "0.1.7", + "--namespace", "shapeblock", + "--timeout", "600s"); err != nil { + return fmt.Errorf("failed to install kpack: %v", err) + } + + printStatus("kpack installed successfully") + return nil +} + +func installKpackResources() error { + printStatus("Installing/Updating kpack resources...") + + resources := []string{ + "assets/kpack/cluster-stack.yaml", + "assets/kpack/go-clusterstore.yaml", + "assets/kpack/nginx-clusterstore.yaml", + "assets/kpack/node-clusterstore.yaml", + "assets/kpack/python-clusterstore.yaml", + "assets/kpack/ruby-clusterstore.yaml", + "assets/kpack/sb-php-clusterstore.yaml", + } + + for _, resource := range resources { + // Read embedded file + content, err := kpackAssets.ReadFile(resource) + if err != nil { + printError(fmt.Sprintf("Failed to read resource %s: %v", resource, err)) + return err + } + + // Create temporary file + tmpfile, err := os.CreateTemp("", "kpack-*.yaml") + if err != nil { + printError(fmt.Sprintf("Failed to create temp file: %v", err)) + return err + } + defer os.Remove(tmpfile.Name()) + + // Write content to temp file + if _, err := tmpfile.Write(content); err != nil { + printError(fmt.Sprintf("Failed to write to temp file: %v", err)) + return err + } + tmpfile.Close() + + // Apply the resource + if err := runCommand("kubectl", "apply", "-f", tmpfile.Name()); err != nil { + printError(fmt.Sprintf("Failed to install kpack resource %s: %v", resource, err)) + return err + } + } + + printStatus("kpack resources installed successfully") + return nil +} From 36f07031e8850126fa09a92a31d5d249071f6b94 Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Mon, 30 Dec 2024 13:52:01 +0530 Subject: [PATCH 4/7] Flux2 Helm operator and Helm repository custom resources. --- CHANGELOG.md | 2 + assets/helm-repos/bitnami-repository.yaml | 8 ++ assets/helm-repos/sb-repository.yaml | 8 ++ main.go | 91 +++++++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 assets/helm-repos/bitnami-repository.yaml create mode 100644 assets/helm-repos/sb-repository.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3386e..5b16fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Install container registry - Install kpack - Install kpack cluster stores and cluster stacks +- Install Flux2 Helm operator +- Install Helm repository custom resources ### Changed diff --git a/assets/helm-repos/bitnami-repository.yaml b/assets/helm-repos/bitnami-repository.yaml new file mode 100644 index 0000000..89e59cb --- /dev/null +++ b/assets/helm-repos/bitnami-repository.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: bitnami + namespace: shapeblock +spec: + interval: 1m + url: https://charts.bitnami.com/bitnami diff --git a/assets/helm-repos/sb-repository.yaml b/assets/helm-repos/sb-repository.yaml new file mode 100644 index 0000000..250dea6 --- /dev/null +++ b/assets/helm-repos/sb-repository.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: shapeblock + namespace: shapeblock +spec: + interval: 1m + url: https://shapeblock.github.io diff --git a/main.go b/main.go index bfbdf38..47daf58 100644 --- a/main.go +++ b/main.go @@ -57,6 +57,9 @@ var dashboardAssets embed.FS //go:embed assets/kpack/* var kpackAssets embed.FS +//go:embed assets/helm-repos/* +var helmReposAssets embed.FS + // Add this near the top of the file with other constants var githubToken string // Will be set during compilation @@ -968,10 +971,21 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to install kpack: %v", err) } + // Install kpack resources if err := installKpackResources(); err != nil { return fmt.Errorf("failed to install kpack resources: %v", err) } + // Install Flux2 Helm operator + if err := installFlux2(); err != nil { + return fmt.Errorf("failed to install Flux2: %v", err) + } + + // Install Helm repository custom resources + if err := installHelmRepos(); err != nil { + return fmt.Errorf("failed to install Helm repository custom resources: %v", err) + } + if err := installPrometheusStack(config); err != nil { return fmt.Errorf("failed to install Prometheus Stack: %v", err) } @@ -3316,3 +3330,80 @@ func installKpackResources() error { printStatus("kpack resources installed successfully") return nil } + +func installFlux2() error { + printStatus("Installing Flux2 Helm operator...") + + // Check if Flux2 already exists + if resourceExists("deployment", "helm-operator-flux2-helm-controller", "shapeblock") { + printStatus("Flux2 Helm operator already installed") + return nil + } + + // Add fluxcd-community helm repo + if err := addHelmRepo("fluxcd-community", "https://fluxcd-community.github.io/helm-charts"); err != nil { + return err + } + + // Install Flux2 using helm + if err := runCommand("helm", "install", "helm-operator", + "fluxcd-community/flux2", + "--version", "2.13.0", + "--namespace", "shapeblock", + "--set", "imageAutomationController.create=false", + "--set", "imageReflectorController.create=false", + "--set", "kustomizeController.create=false", + "--timeout", "600s"); err != nil { + return fmt.Errorf("failed to install Flux2: %v", err) + } + + printStatus("Flux2 Helm operator installed successfully") + return nil +} + +func installHelmRepos() error { + printStatus("Installing Helm repository custom resources...") + + // Read the directory contents + entries, err := helmReposAssets.ReadDir("assets/helm-repos") + if err != nil { + return fmt.Errorf("failed to read helm repos directory: %v", err) + } + + for _, entry := range entries { + if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".yaml") { + // Read embedded file + content, err := helmReposAssets.ReadFile(fmt.Sprintf("assets/helm-repos/%s", entry.Name())) + if err != nil { + printError(fmt.Sprintf("Failed to read helm repo CR %s: %v", entry.Name(), err)) + return err + } + + // Create temporary file + tmpfile, err := os.CreateTemp("", "helm-repo-*.yaml") + if err != nil { + printError(fmt.Sprintf("Failed to create temp file: %v", err)) + return err + } + defer os.Remove(tmpfile.Name()) + + // Write content to temp file + if _, err := tmpfile.Write(content); err != nil { + printError(fmt.Sprintf("Failed to write to temp file: %v", err)) + return err + } + tmpfile.Close() + + // Apply the resource + if err := runCommand("kubectl", "apply", "-f", tmpfile.Name(), "-n", "shapeblock"); err != nil { + printError(fmt.Sprintf("Failed to install helm repo CR %s: %v", entry.Name(), err)) + return err + } + + printStatus(fmt.Sprintf("Installed helm repo CR: %s", entry.Name())) + } + } + + printStatus("Helm repository custom resources installed successfully") + return nil +} From 17bf1e3706ef474e321e6b571d3aec087a904151 Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Mon, 30 Dec 2024 17:15:58 +0530 Subject: [PATCH 5/7] SB operator. --- CHANGELOG.md | 2 +- main.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b16fa1..558d16b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Install kpack cluster stores and cluster stacks - Install Flux2 Helm operator - Install Helm repository custom resources - +- Install ShapeBlock operator ### Changed - Fix cron job creation diff --git a/main.go b/main.go index 47daf58..8158dc1 100644 --- a/main.go +++ b/main.go @@ -986,6 +986,10 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to install Helm repository custom resources: %v", err) } + if err := installSBOperator(config); err != nil { + return fmt.Errorf("failed to install ShapeBlock operator: %v", err) + } + if err := installPrometheusStack(config); err != nil { return fmt.Errorf("failed to install Prometheus Stack: %v", err) } @@ -3407,3 +3411,97 @@ func installHelmRepos() error { printStatus("Helm repository custom resources installed successfully") return nil } + +func installSBOperator(config *Config) error { + printStatus("Installing ShapeBlock operator...") + + // Check if operator already exists + if resourceExists("deployment", "sb-operator", "shapeblock") { + printStatus("ShapeBlock operator already installed") + return nil + } + + // Get domain or use IP if not set + domain := config.DomainName + if domain == "" { + ip, err := getNodeIP() + if err != nil { + return fmt.Errorf("failed to get node IP: %v", err) + } + domain = fmt.Sprintf("%s.nip.io", ip) + } + + // Create operator YAML + operatorYAML := fmt.Sprintf(`apiVersion: v1 +kind: ServiceAccount +metadata: + name: shapeblock-admin + namespace: shapeblock +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: shapeblock-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: shapeblock-admin + namespace: shapeblock +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sb-operator + namespace: shapeblock +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + application: sb-operator + template: + metadata: + labels: + application: sb-operator + spec: + serviceAccountName: shapeblock-admin + containers: + - name: sb-operator + image: shapeblock/sb-operator:20-dec-2024.1 + imagePullPolicy: Always + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 30 + env: + - name: SB_URL + value: https://api.%s`, domain) + + // Write YAML to temporary file + tmpfile, err := os.CreateTemp("", "sb-operator-*.yaml") + if err != nil { + return fmt.Errorf("failed to create temp file: %v", err) + } + defer os.Remove(tmpfile.Name()) + + if _, err := tmpfile.WriteString(operatorYAML); err != nil { + return fmt.Errorf("failed to write operator YAML: %v", err) + } + tmpfile.Close() + + // Apply the operator resources + if err := runCommand("kubectl", "apply", "-f", tmpfile.Name()); err != nil { + return fmt.Errorf("failed to install ShapeBlock operator: %v", err) + } + + printStatus("ShapeBlock operator installed successfully") + return nil +} From fe50b8c15397160344b6df9b6840912d9f7ce054 Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Mon, 30 Dec 2024 18:57:19 +0530 Subject: [PATCH 6/7] Operator and CRDs. --- CHANGELOG.md | 3 + assets/shapeblock/application-crd.yaml | 87 ++++++++++++++++++++++++ assets/shapeblock/project-crd.yaml | 37 +++++++++++ main.go | 91 ++++++++++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 assets/shapeblock/application-crd.yaml create mode 100644 assets/shapeblock/project-crd.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 558d16b..279fb49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Install Flux2 Helm operator - Install Helm repository custom resources - Install ShapeBlock operator +- Registry credentials secret +- App and project CRDs + ### Changed - Fix cron job creation diff --git a/assets/shapeblock/application-crd.yaml b/assets/shapeblock/application-crd.yaml new file mode 100644 index 0000000..d8f65a0 --- /dev/null +++ b/assets/shapeblock/application-crd.yaml @@ -0,0 +1,87 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: applications.dev.shapeblock.com +spec: + group: dev.shapeblock.com + names: + kind: Application + listKind: ApplicationList + shortNames: + - app + - apps + plural: applications + singular: application + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Application is the Schema for the applications API + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + description: ApplicationSpec defines the desired state of Application + properties: + stack: + type: string + enum: [php, java, python, node, go, ruby, nginx] + chart: + properties: + name: + type: string + repo: + type: string + values: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + build: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + type: object + git: + properties: + ref: + type: string + repo: + type: string + subPath: + type: string + type: object + serviceAccount: + type: string + tag: + type: string + type: object + status: + x-kubernetes-preserve-unknown-fields: true + description: ApplicationStatus defines the observed state of Application + properties: + deployed: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: boolean + image: + type: string + ref: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/assets/shapeblock/project-crd.yaml b/assets/shapeblock/project-crd.yaml new file mode 100644 index 0000000..540b448 --- /dev/null +++ b/assets/shapeblock/project-crd.yaml @@ -0,0 +1,37 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: projects.dev.shapeblock.com +spec: + group: dev.shapeblock.com + names: + kind: Project + listKind: ProjectList + plural: projects + singular: project + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + name: + type: string + description: The human readable name of the project. + description: + type: string + description: What this project is about. + type: object + served: true + storage: true + subresources: + status: {} diff --git a/main.go b/main.go index 8158dc1..9cd7ec8 100644 --- a/main.go +++ b/main.go @@ -60,6 +60,9 @@ var kpackAssets embed.FS //go:embed assets/helm-repos/* var helmReposAssets embed.FS +//go:embed assets/shapeblock/* +var shapeblockcrdAssets embed.FS + // Add this near the top of the file with other constants var githubToken string // Will be set during compilation @@ -1013,6 +1016,11 @@ func install(config *Config, backendConfig *BackendConfig) error { } } } + + if err := installShapeBlockCRDs(); err != nil { + return fmt.Errorf("failed to install ShapeBlock CRDs: %v", err) + } + return nil } @@ -3246,6 +3254,42 @@ func installRegistry(config *Config) error { return fmt.Errorf("failed to install registry: %v", err) } + // Create registry credentials secret + dockerConfig := fmt.Sprintf(`{ + "auths": { + "%s": { + "auth": "%s" + } + } + }`, registryDomain, base64.StdEncoding.EncodeToString([]byte("shapeblock:"+password))) + + // Create temporary file for the secret + tmpfile, err := os.CreateTemp("", "registry-secret-*.yaml") + if err != nil { + return fmt.Errorf("failed to create temp file: %v", err) + } + defer os.Remove(tmpfile.Name()) + + secretYAML := fmt.Sprintf(`apiVersion: v1 +kind: Secret +metadata: + name: registry-creds + namespace: shapeblock +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: %s +`, base64.StdEncoding.EncodeToString([]byte(dockerConfig))) + + if _, err := tmpfile.WriteString(secretYAML); err != nil { + return fmt.Errorf("failed to write secret YAML: %v", err) + } + tmpfile.Close() + + // Apply the secret + if err := runCommand("kubectl", "apply", "-f", tmpfile.Name()); err != nil { + return fmt.Errorf("failed to create registry credentials secret: %v", err) + } + printStatus(fmt.Sprintf("Docker Registry installed successfully at %s", registryDomain)) printStatus("Registry credentials:") printStatus(" Username: shapeblock") @@ -3505,3 +3549,50 @@ spec: printStatus("ShapeBlock operator installed successfully") return nil } + +func installShapeBlockCRDs() error { + printStatus("Installing ShapeBlock CRDs...") + + // Read the directory contents + entries, err := shapeblockcrdAssets.ReadDir("assets/shapeblock") + if err != nil { + return fmt.Errorf("failed to read shapeblock CRDs directory: %v", err) + } + + for _, entry := range entries { + if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".yaml") { + // Read embedded file + content, err := shapeblockcrdAssets.ReadFile(fmt.Sprintf("assets/shapeblock/%s", entry.Name())) + if err != nil { + printError(fmt.Sprintf("Failed to read ShapeBlock CRD %s: %v", entry.Name(), err)) + return err + } + + // Create temporary file + tmpfile, err := os.CreateTemp("", "shapeblock-crd-*.yaml") + if err != nil { + printError(fmt.Sprintf("Failed to create temp file: %v", err)) + return err + } + defer os.Remove(tmpfile.Name()) + + // Write content to temp file + if _, err := tmpfile.Write(content); err != nil { + printError(fmt.Sprintf("Failed to write to temp file: %v", err)) + return err + } + tmpfile.Close() + + // Apply the CRD + if err := runCommand("kubectl", "apply", "-f", tmpfile.Name()); err != nil { + printError(fmt.Sprintf("Failed to install ShapeBlock CRD %s: %v", entry.Name(), err)) + return err + } + + printStatus(fmt.Sprintf("Installed ShapeBlock CRD: %s", entry.Name())) + } + } + + printStatus("ShapeBlock CRDs installed successfully") + return nil +} From 20cee43e6dbaf38616aca9c5bba29af23f3fcb46 Mon Sep 17 00:00:00 2001 From: Lakshmi Narasimhan Date: Thu, 2 Jan 2025 05:46:52 +0530 Subject: [PATCH 7/7] Consolidate postgres and tfstate postgres. --- CHANGELOG.md | 3 + assets/helm-repos/bitnami-repository.yaml | 6 +- main.go | 165 +++++++++++++++------- 3 files changed, 118 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 279fb49..8db7b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,15 +17,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Install ShapeBlock operator - Registry credentials secret - App and project CRDs +- Proper cleanup during uninstall ### Changed - Fix cron job creation +- Unified postgres and tfstate postgres into single instance ### Removed - Removed resource constraints on backend and frontend. - Epinio is no longer installed. +- Celery worker in backend ## [1.0.4] - 2024-12-19 diff --git a/assets/helm-repos/bitnami-repository.yaml b/assets/helm-repos/bitnami-repository.yaml index 89e59cb..c6f591a 100644 --- a/assets/helm-repos/bitnami-repository.yaml +++ b/assets/helm-repos/bitnami-repository.yaml @@ -1,8 +1,10 @@ apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: HelmRepository +kind: OCIRepository metadata: name: bitnami namespace: shapeblock spec: interval: 1m - url: https://charts.bitnami.com/bitnami + url: oci://registry-1.docker.io/bitnamicharts/mysql + ref: + semver: ">12.0.0" diff --git a/main.go b/main.go index 9cd7ec8..21bd99b 100644 --- a/main.go +++ b/main.go @@ -119,7 +119,6 @@ type BackendConfig struct { TFStateUsername string TFStatePassword string TFStateDatabase string - TFStateRootPW string LicenseKey string } @@ -687,11 +686,11 @@ func generatePassword() string { return string(b) } -func installPostgres(name, username, database string, backendConfig *BackendConfig) error { - printStatus(fmt.Sprintf("Installing PostgreSQL instance %s...", name)) +func installPostgres(backendConfig *BackendConfig) error { + printStatus("Installing PostgreSQL...") - if resourceExists("statefulset", name+"-postgresql", "shapeblock") { - printStatus(fmt.Sprintf("PostgreSQL instance %s already installed", name)) + if resourceExists("statefulset", "db-postgresql", "shapeblock") { + printStatus("PostgreSQL already installed") return nil } @@ -699,29 +698,27 @@ func installPostgres(name, username, database string, backendConfig *BackendConf return err } - // Generate passwords - password := generatePassword() + // Generate passwords for both users and root + mainPassword := generatePassword() + tfstatePassword := generatePassword() rootPW := generatePassword() - // Store credentials based on instance name - switch name { - case "db": - backendConfig.PostgresUsername = username - backendConfig.PostgresDatabase = database - backendConfig.PostgresPassword = password - backendConfig.PostgresRootPW = rootPW - case "tfstate": - backendConfig.TFStateUsername = username - backendConfig.TFStateDatabase = database - backendConfig.TFStatePassword = password - backendConfig.TFStateRootPW = rootPW - } + // Store credentials in backend config + backendConfig.PostgresUsername = "shapeblock" + backendConfig.PostgresDatabase = "shapeblock" + backendConfig.PostgresPassword = mainPassword + backendConfig.PostgresRootPW = rootPW + backendConfig.TFStateUsername = "tfstate" + backendConfig.TFStateDatabase = "tfstate" + backendConfig.TFStatePassword = tfstatePassword // Log the credentials for debugging - logMessage("INFO", fmt.Sprintf("PostgreSQL %s credentials - username: %s, database: %s, password: %s", - name, username, database, password)) + logMessage("INFO", fmt.Sprintf("PostgreSQL main credentials - username: %s, database: %s, password: %s", + backendConfig.PostgresUsername, backendConfig.PostgresDatabase, mainPassword)) + logMessage("INFO", fmt.Sprintf("PostgreSQL tfstate credentials - username: %s, database: %s, password: %s", + backendConfig.TFStateUsername, backendConfig.TFStateDatabase, tfstatePassword)) - // Create values.yaml + // Create values with initialization scripts for both databases values := fmt.Sprintf(` auth: database: %s @@ -732,13 +729,42 @@ architecture: standalone primary: persistence: size: 2Gi + initdb: + scripts: + init_db_and_users.sql: | + -- Create tfstate user and database + CREATE USER %s WITH PASSWORD '%s'; + CREATE DATABASE %s OWNER %s; + GRANT ALL PRIVILEGES ON DATABASE %s TO %s; + + -- Create shapeblock user and database + CREATE USER %s WITH PASSWORD '%s'; + CREATE DATABASE %s OWNER %s; + GRANT ALL PRIVILEGES ON DATABASE %s TO %s; tls: enabled: true - autoGenerated: true -`, database, username, password, rootPW) + autoGenerated: true`, + backendConfig.PostgresDatabase, // Initial database + backendConfig.PostgresUsername, // Initial user + backendConfig.PostgresPassword, + backendConfig.PostgresRootPW, + // tfstate database and user creation + backendConfig.TFStateUsername, + backendConfig.TFStatePassword, + backendConfig.TFStateDatabase, + backendConfig.TFStateUsername, + backendConfig.TFStateDatabase, + backendConfig.TFStateUsername, + // shapeblock database and user creation + backendConfig.PostgresUsername, + backendConfig.PostgresPassword, + backendConfig.PostgresDatabase, + backendConfig.PostgresUsername, + backendConfig.PostgresDatabase, + backendConfig.PostgresUsername) // Write values to temporary file - tmpfile, err := os.CreateTemp("", fmt.Sprintf("%s-values-*.yaml", name)) + tmpfile, err := os.CreateTemp("", "postgres-values-*.yaml") if err != nil { printError(fmt.Sprintf("Failed to create temp file: %v", err)) return err @@ -753,16 +779,16 @@ tls: // Install PostgreSQL if err := runCommand("helm", "upgrade", "--install", - name, "bitnami/postgresql", + "db", "bitnami/postgresql", "--version", "16.2.3", "--namespace", "shapeblock", "--values", tmpfile.Name(), "--timeout", "600s"); err != nil { - printError(fmt.Sprintf("Failed to install PostgreSQL %s: %v", name, err)) + printError(fmt.Sprintf("Failed to install PostgreSQL: %v", err)) return err } - printStatus(fmt.Sprintf("PostgreSQL instance %s installed successfully", name)) + printStatus("PostgreSQL installed successfully with both databases") return nil } @@ -775,7 +801,7 @@ func createTerraformSecret(backendConfig *BackendConfig) error { } // Create connection string - connStr := fmt.Sprintf("postgres://%s:%s@tfstate-postgresql/%s", + connStr := fmt.Sprintf("postgres://%s:%s@db-postgresql/%s", backendConfig.TFStateUsername, backendConfig.TFStatePassword, backendConfig.TFStateDatabase) @@ -932,14 +958,9 @@ func install(config *Config, backendConfig *BackendConfig) error { return fmt.Errorf("failed to create service account: %v", err) } - // Install main PostgreSQL instance - if err := installPostgres("db", "shapeblock", "shapeblock", backendConfig); err != nil { - return fmt.Errorf("failed to install main PostgreSQL: %v", err) - } - - // Install TFState PostgreSQL instance - if err := installPostgres("tfstate", "tfstate", "tfstate", backendConfig); err != nil { - return fmt.Errorf("failed to install TFState PostgreSQL: %v", err) + // Install PostgreSQL instance + if err := installPostgres(backendConfig); err != nil { + return fmt.Errorf("failed to install PostgreSQL: %v", err) } // Create Terraform credentials secret @@ -1768,19 +1789,6 @@ deployments: release: backend replicas: 1 - worker: - containers: - - envConfigmaps: - - envs - envSecrets: - - secret-envs - name: worker - command: ['celery', '-A', 'shapeblock', 'worker', '-l', 'INFO'] - podLabels: - app: shapeblock - release: backend - replicas: 1 - envs: DEBUG: "False" DATABASE_URL: "postgres://%s:%s@db-postgresql/%s" @@ -2279,7 +2287,7 @@ Admin Backend Access: - URL: https://api.%s/admin-%s/ - Username: admin - Password: -`, domain, config.AdminEmail, domain, config.AppName) +`, domain, config.AdminEmail, domain, slugify(config.AppName)) // Print to console fmt.Println(instructions) @@ -2411,7 +2419,7 @@ func uninstall() error { // Uninstall PostgreSQL instances printStatus("Uninstalling PostgreSQL instances...") - for _, instance := range []string{"db", "tfstate"} { + for _, instance := range []string{"db"} { if err := runCommand("helm", "uninstall", instance, "-n", "shapeblock"); err != nil { printStatus(fmt.Sprintf("Note: PostgreSQL instance %s was not installed or already removed", instance)) } @@ -2522,6 +2530,55 @@ func uninstall() error { } } + // Clean up files + printStatus("Cleaning up installation files...") + + // Get home directory + homeDir, err := os.UserHomeDir() + if err != nil { + printStatus(fmt.Sprintf("Warning: Failed to get home directory: %v", err)) + } else { + // Remove SSH keys + sshFiles := []string{ + filepath.Join(homeDir, "sb"), + filepath.Join(homeDir, "sb.pub"), + } + + for _, file := range sshFiles { + if err := os.Remove(file); err != nil { + if !os.IsNotExist(err) { + printStatus(fmt.Sprintf("Warning: Failed to remove %s: %v", file, err)) + } + } else { + printStatus(fmt.Sprintf("Removed %s", file)) + } + } + } + + // Remove installation files from current directory + installFiles := []string{ + "instructions.txt", + "kubeconfig", + } + + // Add any install*.log files + matches, err := filepath.Glob("install*.log") + if err != nil { + printStatus(fmt.Sprintf("Warning: Failed to find log files: %v", err)) + } else { + installFiles = append(installFiles, matches...) + } + + for _, file := range installFiles { + if err := os.Remove(file); err != nil { + if !os.IsNotExist(err) { + printStatus(fmt.Sprintf("Warning: Failed to remove %s: %v", file, err)) + } + } else { + printStatus(fmt.Sprintf("Removed %s", file)) + } + } + printStatus("ShapeBlock has been successfully uninstalled") return nil } @@ -3515,7 +3572,7 @@ spec: serviceAccountName: shapeblock-admin containers: - name: sb-operator - image: shapeblock/sb-operator:20-dec-2024.1 + image: ghcr.io/shapeblock/operator:v2 imagePullPolicy: Always livenessProbe: failureThreshold: 3