From 7e4f0e937ebd1fea21595e6f090e6378e167cff1 Mon Sep 17 00:00:00 2001 From: Stefan Martinov Date: Sun, 1 Mar 2020 14:11:20 +0100 Subject: [PATCH 01/18] feat: refactor cobra commands --- README.md | 4 +-- cmd/actions/build.go | 19 +++++++++++++ cmd/actions/deploy.go | 20 ++++++++++++++ cmd/actions/root.go | 25 +++++++++++++++++ cmd/main.go | 27 ++----------------- configurd.go | 26 +++--------------- example/Makefile | 5 +++- .../templates/services/hello-service.yml | 0 8 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 cmd/actions/deploy.go create mode 100644 cmd/actions/root.go delete mode 100644 example/configurd/templates/services/hello-service.yml diff --git a/README.md b/README.md index 97cef0c..0749b4f 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Application for managing kubernetes microservice environment ```text /configurd - /templates (Helm Templates) - /services (Charts) + /charts (Helm Charts) + /services /applications /services service-a.yaml diff --git a/cmd/actions/build.go b/cmd/actions/build.go index ac24c55..dcf105d 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -3,10 +3,29 @@ package actions import ( "context" "log" + "os" "github.com/foomo/configurd" + "github.com/spf13/cobra" ) +var cmdBuild = &cobra.Command{ + Use: "build [configuration file to use for service group] -t TAG", + Short: "Add a service group", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + dir, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + Build(dir, args[0], "latest") + }, +} + +func init() { + rootCmd.AddCommand(cmdBuild) +} + func Build(dir, service, tag string) { cnf, err := configurd.New(dir) if err != nil { diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go new file mode 100644 index 0000000..9d213ab --- /dev/null +++ b/cmd/actions/deploy.go @@ -0,0 +1,20 @@ +package actions + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(deployCmd) +} + +var deployCmd = &cobra.Command{ + Use: "deploy", + Short: "Print the version number of Hugo", + Long: `All software has versions. This is Hugo's`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") + }, +} diff --git a/cmd/actions/root.go b/cmd/actions/root.go new file mode 100644 index 0000000..1d10548 --- /dev/null +++ b/cmd/actions/root.go @@ -0,0 +1,25 @@ +package actions + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var ( + rootCmd = &cobra.Command{ + Use: "cobra", + Short: "A generator for Cobra based Applications", + Long: `Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + } +) + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/cmd/main.go b/cmd/main.go index f5bb9f6..9398080 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,30 +1,7 @@ package main -import ( - "log" - "os" - - "github.com/foomo/configurd/cmd/actions" - "github.com/spf13/cobra" -) +import "github.com/foomo/configurd/cmd/actions" func main() { - dir, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - - var cmdBuild = &cobra.Command{ - Use: "build [configuration file to use for service group] -t TAG", - Short: "Add a service group", - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - actions.Build(dir, args[0], "latest") - }, - } - - var rootCmd = &cobra.Command{Use: "app"} - rootCmd.AddCommand(cmdBuild) - - _ = rootCmd.Execute() + actions.Execute() } diff --git a/configurd.go b/configurd.go index 00ae87a..e0c8e80 100644 --- a/configurd.go +++ b/configurd.go @@ -9,10 +9,9 @@ import ( ) const ( - defaultConfigFileExt = ".yml" - defaultServiceDir = "configurd/services" - defaultTemplateServiceDir = "configurd/templates/services" - defaultNamespaceDir = "configurd/namespaces" + defaultConfigFileExt = ".yml" + defaultServiceDir = "configurd/services" + defaultNamespaceDir = "configurd/namespaces" ) var ( @@ -49,29 +48,10 @@ func New(dir string) (Configurd, error) { return nil }) - // Load Templates - err = filepath.Walk(path.Join(defaultTemplateServiceDir), func(path string, info os.FileInfo, err error) error { - if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { - c.Templates = append(c.Templates, strings.TrimSuffix(info.Name(), defaultConfigFileExt)) - } - return nil - }) - - // Load Namespaces - namespaceDir := path.Join(dir, defaultNamespaceDir) - err = filepath.Walk(namespaceDir, func(path string, info os.FileInfo, err error) error { - if info.IsDir() && path != namespaceDir { - c.Namespaces = append(c.Namespaces, Namespace{Name: info.Name()}) - } - return nil - }) - if err != nil { return Configurd{}, err } - // Validate Connections - return c, nil } diff --git a/example/Makefile b/example/Makefile index 4d6407b..b7ebd55 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,2 +1,5 @@ build: - go run ../cmd/main.go build hello-service \ No newline at end of file + go run ../cmd/main.go build hello-service + +deploy: + go run ../cmd/main.go deploy \ No newline at end of file diff --git a/example/configurd/templates/services/hello-service.yml b/example/configurd/templates/services/hello-service.yml deleted file mode 100644 index e69de29..0000000 From 6c1ecf530ce053c1948387b36ff1a39c830874ec Mon Sep 17 00:00:00 2001 From: Stefan Martinov Date: Sun, 1 Mar 2020 14:15:20 +0100 Subject: [PATCH 02/18] feat: add chart to service --- docker.go | 1 + docker_test.go | 2 ++ example/configurd/deploy/.gitkeep | 0 example/configurd/services/hello-service.yml | 1 + 4 files changed, 4 insertions(+) create mode 100644 example/configurd/deploy/.gitkeep diff --git a/docker.go b/docker.go index f675881..0b00a18 100644 --- a/docker.go +++ b/docker.go @@ -13,6 +13,7 @@ import ( type Service struct { Name string `yaml:"name"` Docker Docker `yaml:"docker"` + Chart string `yaml:"chart"` } type Docker struct { diff --git a/docker_test.go b/docker_test.go index 32073a4..823d56d 100644 --- a/docker_test.go +++ b/docker_test.go @@ -21,6 +21,7 @@ func TestLoadService(t *testing.T) { Options: "", Image: "foomo/configurd-hello", }, + Chart: "example", } actual, err := LoadService(file) @@ -37,6 +38,7 @@ func TestService_Build(t *testing.T) { Options: "", Image: "configurd-hello", }, + Chart: "example", } output, err := svc.Build(context.Background(), "latest") diff --git a/example/configurd/deploy/.gitkeep b/example/configurd/deploy/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/example/configurd/services/hello-service.yml b/example/configurd/services/hello-service.yml index 87ce4bb..7e7051f 100644 --- a/example/configurd/services/hello-service.yml +++ b/example/configurd/services/hello-service.yml @@ -5,3 +5,4 @@ service: file: Dockerfile context: application # options: + chart: example From 25109aa68cfb1432127aec986c5e8f473104a403 Mon Sep 17 00:00:00 2001 From: Stefan Martinov Date: Mon, 2 Mar 2020 10:28:30 +0100 Subject: [PATCH 03/18] feat: add WIP for deploy process --- cmd/actions/build.go | 14 +--- cmd/actions/deploy.go | 31 ++++++--- cmd/actions/root.go | 19 ++++++ configurd.go | 11 +--- example/.gitignore | 1 + example/Makefile | 4 +- example/configurd/charts/example/.helmignore | 22 +++++++ example/configurd/charts/example/Chart.yaml | 5 ++ .../charts/example/templates/NOTES.txt | 21 ++++++ .../charts/example/templates/_helpers.tpl | 32 +++++++++ .../charts/example/templates/deployment.yaml | 51 +++++++++++++++ .../charts/example/templates/ingress.yaml | 40 ++++++++++++ .../charts/example/templates/service.yaml | 19 ++++++ .../templates/tests/test-connection.yaml | 18 +++++ example/configurd/charts/example/values.yaml | 48 ++++++++++++++ example/configurd/deploy/.gitkeep | 0 .../namespaces/local/hello-deployment.yml | 2 + .../namespaces/local/hello-service.yml | 0 go.mod | 1 + go.sum | 4 ++ helm.go | 65 +++++++++++++++++++ helm_test.go | 19 ++++++ 22 files changed, 396 insertions(+), 31 deletions(-) create mode 100644 example/.gitignore create mode 100644 example/configurd/charts/example/.helmignore create mode 100644 example/configurd/charts/example/Chart.yaml create mode 100644 example/configurd/charts/example/templates/NOTES.txt create mode 100644 example/configurd/charts/example/templates/_helpers.tpl create mode 100644 example/configurd/charts/example/templates/deployment.yaml create mode 100644 example/configurd/charts/example/templates/ingress.yaml create mode 100644 example/configurd/charts/example/templates/service.yaml create mode 100644 example/configurd/charts/example/templates/tests/test-connection.yaml create mode 100644 example/configurd/charts/example/values.yaml delete mode 100644 example/configurd/deploy/.gitkeep create mode 100644 example/configurd/namespaces/local/hello-deployment.yml delete mode 100644 example/configurd/namespaces/local/hello-service.yml create mode 100644 helm.go create mode 100644 helm_test.go diff --git a/cmd/actions/build.go b/cmd/actions/build.go index dcf105d..f97d349 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -3,9 +3,7 @@ package actions import ( "context" "log" - "os" - "github.com/foomo/configurd" "github.com/spf13/cobra" ) @@ -14,11 +12,7 @@ var cmdBuild = &cobra.Command{ Short: "Add a service group", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - dir, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - Build(dir, args[0], "latest") + Build(args[0], cmd.Flag("tag").Value.String()) }, } @@ -26,11 +20,7 @@ func init() { rootCmd.AddCommand(cmdBuild) } -func Build(dir, service, tag string) { - cnf, err := configurd.New(dir) - if err != nil { - log.Fatal(err) - } +func Build(service, tag string) { svc, err := cnf.Service(service) if err != nil { diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index 9d213ab..16296b6 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -1,20 +1,33 @@ package actions import ( - "fmt" - "github.com/spf13/cobra" ) func init() { rootCmd.AddCommand(deployCmd) + deployCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build deployment before publishing") } -var deployCmd = &cobra.Command{ - Use: "deploy", - Short: "Print the version number of Hugo", - Long: `All software has versions. This is Hugo's`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") - }, +var ( + flagBuild bool +) + +var ( + deployCmd = &cobra.Command{ + Use: "deploy [NAMESPACE] [DEPLOYMENT] -t {TAG}", + Short: "Deploys the specified deployment with given tag version", + Long: `Deploys the specified deployment with given tag version`, + Args: cobra.MinimumNArgs(2), + Run: func(cmd *cobra.Command, args []string) { + if flagBuild { + //TODO: Get All Services From Deployment and build them + } + Deploy(args[0], args[1], FlagTag) + }, + } +) + +func Deploy(namespace, deployment, tag string) { + cnf.Deploy(namespace, deployment, tag) } diff --git a/cmd/actions/root.go b/cmd/actions/root.go index 1d10548..d2482f7 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -2,12 +2,15 @@ package actions import ( "fmt" + "log" "os" + "github.com/foomo/configurd" "github.com/spf13/cobra" ) var ( + cnf configurd.Configurd rootCmd = &cobra.Command{ Use: "cobra", Short: "A generator for Cobra based Applications", @@ -15,9 +18,25 @@ var ( This application is a tool to generate the needed files to quickly create a Cobra application.`, } + + FlagTag string ) +func init() { + rootCmd.PersistentFlags().StringVarP(&FlagTag, "tag", "t", "latest", "Specifies the image tag") +} + func Execute() { + dir, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + + cnf, err = configurd.New(dir) + if err != nil { + log.Fatal(err) + } + if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) diff --git a/configurd.go b/configurd.go index e0c8e80..a7011f1 100644 --- a/configurd.go +++ b/configurd.go @@ -33,6 +33,9 @@ func New(dir string) (Configurd, error) { serviceDir := path.Join(dir, defaultServiceDir) err := filepath.Walk(serviceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { file, err := os.Open(path) if err != nil { @@ -63,11 +66,3 @@ func (c Configurd) Service(name string) (Service, error) { } return Service{}, ErrServiceNotFound } - -func (c Configurd) NamespaceHasService(basePath string, namespaceName string, serviceName string) bool { - p := path.Join(basePath, defaultNamespaceDir, namespaceName, serviceName+defaultConfigFileExt) - if _, err := os.Stat(p); err != nil && os.IsNotExist(err) { - return false - } - return true -} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..e6bc508 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1 @@ +deployments \ No newline at end of file diff --git a/example/Makefile b/example/Makefile index b7ebd55..c7e1e21 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,5 +1,5 @@ build: - go run ../cmd/main.go build hello-service + go run ../cmd/main.go build hello-service -t master deploy: - go run ../cmd/main.go deploy \ No newline at end of file + go run ../cmd/main.go deploy local hello-service -t master \ No newline at end of file diff --git a/example/configurd/charts/example/.helmignore b/example/configurd/charts/example/.helmignore new file mode 100644 index 0000000..50af031 --- /dev/null +++ b/example/configurd/charts/example/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/example/configurd/charts/example/Chart.yaml b/example/configurd/charts/example/Chart.yaml new file mode 100644 index 0000000..f15622e --- /dev/null +++ b/example/configurd/charts/example/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: An Example Chart for Configurd +name: example +version: 0.1.0 diff --git a/example/configurd/charts/example/templates/NOTES.txt b/example/configurd/charts/example/templates/NOTES.txt new file mode 100644 index 0000000..a68a77e --- /dev/null +++ b/example/configurd/charts/example/templates/NOTES.txt @@ -0,0 +1,21 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range $.Values.ingress.paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ . }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "example.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ include "example.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "example.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "example.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/example/configurd/charts/example/templates/_helpers.tpl b/example/configurd/charts/example/templates/_helpers.tpl new file mode 100644 index 0000000..e2907af --- /dev/null +++ b/example/configurd/charts/example/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "example.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "example.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "example.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/example/configurd/charts/example/templates/deployment.yaml b/example/configurd/charts/example/templates/deployment.yaml new file mode 100644 index 0000000..028cb70 --- /dev/null +++ b/example/configurd/charts/example/templates/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "example.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "example.name" . }} + helm.sh/chart: {{ include "example.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "example.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "example.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/example/configurd/charts/example/templates/ingress.yaml b/example/configurd/charts/example/templates/ingress.yaml new file mode 100644 index 0000000..bfb9fc3 --- /dev/null +++ b/example/configurd/charts/example/templates/ingress.yaml @@ -0,0 +1,40 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "example.fullname" . -}} +{{- $ingressPaths := .Values.ingress.paths -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app.kubernetes.io/name: {{ include "example.name" . }} + helm.sh/chart: {{ include "example.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . | quote }} + http: + paths: + {{- range $ingressPaths }} + - path: {{ . }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} + {{- end }} +{{- end }} diff --git a/example/configurd/charts/example/templates/service.yaml b/example/configurd/charts/example/templates/service.yaml new file mode 100644 index 0000000..7528fee --- /dev/null +++ b/example/configurd/charts/example/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "example.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "example.name" . }} + helm.sh/chart: {{ include "example.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: {{ include "example.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/example/configurd/charts/example/templates/tests/test-connection.yaml b/example/configurd/charts/example/templates/tests/test-connection.yaml new file mode 100644 index 0000000..29c7bec --- /dev/null +++ b/example/configurd/charts/example/templates/tests/test-connection.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "example.fullname" . }}-test-connection" + labels: + app.kubernetes.io/name: {{ include "example.name" . }} + helm.sh/chart: {{ include "example.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "example.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/example/configurd/charts/example/values.yaml b/example/configurd/charts/example/values.yaml new file mode 100644 index 0000000..04cf993 --- /dev/null +++ b/example/configurd/charts/example/values.yaml @@ -0,0 +1,48 @@ +# Default values for example. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: foomo/configurd-hello + tag: master + pullPolicy: IfNotPresent # Must be set when working with a local cluster + +nameOverride: "example" +fullnameOverride: "example" + +service: + type: NodePort + port: 8080 + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + paths: [] + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi +# requests: +# cpu: 100m +# memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/example/configurd/deploy/.gitkeep b/example/configurd/deploy/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/example/configurd/namespaces/local/hello-deployment.yml b/example/configurd/namespaces/local/hello-deployment.yml new file mode 100644 index 0000000..8beea44 --- /dev/null +++ b/example/configurd/namespaces/local/hello-deployment.yml @@ -0,0 +1,2 @@ +deployments: + - service: "hello-service" \ No newline at end of file diff --git a/example/configurd/namespaces/local/hello-service.yml b/example/configurd/namespaces/local/hello-service.yml deleted file mode 100644 index e69de29..0000000 diff --git a/go.mod b/go.mod index 25229f1..01654b3 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/foomo/configurd go 1.14 require ( + github.com/otiai10/copy v1.0.2 github.com/spf13/cobra v0.0.6 github.com/stretchr/testify v1.2.2 gopkg.in/yaml.v2 v2.2.8 diff --git a/go.sum b/go.sum index 61f5001..f942c05 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= +github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= diff --git a/helm.go b/helm.go new file mode 100644 index 0000000..00076d9 --- /dev/null +++ b/helm.go @@ -0,0 +1,65 @@ +package configurd + +import ( + "fmt" + "os" + "path" + "path/filepath" + + "github.com/otiai10/copy" +) + +type ServiceDeployment struct { + Name string + ServiceName string `yaml:"service"` + Overrides interface{} +} + +func generate(deployment, outputDir, chartPath string) error { + dir := path.Join(outputDir, deployment) + if err := os.MkdirAll(dir, 0744); err != nil { + return fmt.Errorf("could not create output dir: %w", err) + } + + err := filepath.Walk(chartPath, func(source string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if chartPath == source { + return nil + } + if err := copy.Copy(source, path.Join(dir, info.Name())); err != nil { + return err + } + if info.IsDir() { + return filepath.SkipDir + } + return nil + }) + if err != nil { + return fmt.Errorf("could not copy template files: %w", err) + } + + //TODO: Write overrides into overrides.yaml (global) + + return nil +} + +func (c Configurd) Deploy(namespace, deployment, tag string) (string, error) { + //TODO: Generate Output YAML file + output := "deployments" + if err := os.RemoveAll(output); err != nil { + return "", fmt.Errorf("coudl not clean deployment directory: %w", err) + } + + if err := os.MkdirAll("deployment", 0744); err != nil { + return "", fmt.Errorf("could not create a deployment directory: %w", err) + } + + err := generate(deployment, output, "configurd/charts/example") + if err != nil { + return "", err + } + + return "", nil +} diff --git a/helm_test.go b/helm_test.go new file mode 100644 index 0000000..b480969 --- /dev/null +++ b/helm_test.go @@ -0,0 +1,19 @@ +package configurd + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServiceDeployment_Upgrade(t *testing.T) { + os.Chdir("example") + cnf, err := New(".") + require.NoError(t, err) + + output, err := cnf.Deploy("local", "hello-deployment", "master") + assert.NoError(t, err) + t.Log(output) +} From 48bd38414b1eed002c8abf9720057874b6289591 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Mon, 2 Mar 2020 17:33:14 +0100 Subject: [PATCH 04/18] Added service deployment parsing and override file generation --- example/Makefile | 2 +- example/configurd/namespaces/local/hello-deployment.yml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/example/Makefile b/example/Makefile index c7e1e21..12039da 100644 --- a/example/Makefile +++ b/example/Makefile @@ -2,4 +2,4 @@ build: go run ../cmd/main.go build hello-service -t master deploy: - go run ../cmd/main.go deploy local hello-service -t master \ No newline at end of file + go run ../cmd/main.go deploy local hello-deployment -t master \ No newline at end of file diff --git a/example/configurd/namespaces/local/hello-deployment.yml b/example/configurd/namespaces/local/hello-deployment.yml index 8beea44..b8a9edb 100644 --- a/example/configurd/namespaces/local/hello-deployment.yml +++ b/example/configurd/namespaces/local/hello-deployment.yml @@ -1,2 +1,6 @@ -deployments: - - service: "hello-service" \ No newline at end of file +services: + - service: "hello-service" + overrides: + image: + repository: foomo/configurd-hello + tag: hehe \ No newline at end of file From a0e477d3e7c6d0b645db7de6ac8da6e28a3590c4 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 3 Mar 2020 09:53:01 +0100 Subject: [PATCH 05/18] Added missing files --- cmd/actions/deploy.go | 18 ++++++++++---- cmd/actions/root.go | 5 ++-- configurd.go | 23 +++++++++++++----- docker.go | 21 ++++------------ go.mod | 4 +++- go.sum | 39 ++++++++++++++++++++++++++++++ helm.go | 56 ++++++++++++++++++++++++++++++++++--------- 7 files changed, 125 insertions(+), 41 deletions(-) diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index 16296b6..774e663 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -1,6 +1,8 @@ package actions import ( + "log" + "github.com/spf13/cobra" ) @@ -20,14 +22,22 @@ var ( Long: `Deploys the specified deployment with given tag version`, Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { - if flagBuild { - //TODO: Get All Services From Deployment and build them - } Deploy(args[0], args[1], FlagTag) }, } ) func Deploy(namespace, deployment, tag string) { - cnf.Deploy(namespace, deployment, tag) + sds, err := cnf.ServiceDeployments(cwdir, namespace, deployment) + if err != nil { + log.Fatalf("could not deploy: %v output:\n%v", deployment, err) + } + if flagBuild { + for _, serviceDeployment := range sds { + Build(serviceDeployment.ServiceName, FlagTag) + } + } + for _, serviceDeployment := range sds { + cnf.Deploy(serviceDeployment, namespace, tag) + } } diff --git a/cmd/actions/root.go b/cmd/actions/root.go index d2482f7..3452199 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -11,6 +11,7 @@ import ( var ( cnf configurd.Configurd + cwdir string rootCmd = &cobra.Command{ Use: "cobra", Short: "A generator for Cobra based Applications", @@ -27,12 +28,12 @@ func init() { } func Execute() { - dir, err := os.Getwd() + cwdir, err := os.Getwd() if err != nil { log.Fatal(err) } - cnf, err = configurd.New(dir) + cnf, err = configurd.New(cwdir) if err != nil { log.Fatal(err) } diff --git a/configurd.go b/configurd.go index a7011f1..dfc9cb0 100644 --- a/configurd.go +++ b/configurd.go @@ -2,10 +2,14 @@ package configurd import ( "errors" + "fmt" + "io" "os" "path" "path/filepath" "strings" + + "gopkg.in/yaml.v2" ) const ( @@ -18,19 +22,16 @@ var ( ErrServiceNotFound = errors.New("service not found") ) -type Namespace struct { - Name string -} - type Configurd struct { Services []Service Templates []string - Namespaces []Namespace + Namespaces []string } func New(dir string) (Configurd, error) { c := Configurd{} + //load services serviceDir := path.Join(dir, defaultServiceDir) err := filepath.Walk(serviceDir, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -42,7 +43,7 @@ func New(dir string) (Configurd, error) { return err } defer file.Close() - svc, err := LoadService(file) + svc, err := loadService(file) if err != nil { return err } @@ -66,3 +67,13 @@ func (c Configurd) Service(name string) (Service, error) { } return Service{}, ErrServiceNotFound } + +func loadService(reader io.Reader) (Service, error) { + var wrapper struct { + Service Service `yaml:"service"` + } + if err := yaml.NewDecoder(reader).Decode(&wrapper); err != nil { + return Service{}, fmt.Errorf("could not decode service: %w", err) + } + return wrapper.Service, nil +} diff --git a/docker.go b/docker.go index 0b00a18..463359b 100644 --- a/docker.go +++ b/docker.go @@ -3,19 +3,10 @@ package configurd import ( "context" "fmt" - "io" "os/exec" "strings" - - "gopkg.in/yaml.v2" ) -type Service struct { - Name string `yaml:"name"` - Docker Docker `yaml:"docker"` - Chart string `yaml:"chart"` -} - type Docker struct { File string `yaml:"file"` Context string `yaml:"context"` @@ -23,14 +14,10 @@ type Docker struct { Image string `yaml:"image"` } -func LoadService(reader io.Reader) (Service, error) { - var wrapper struct { - Service Service `yaml:"service"` - } - if err := yaml.NewDecoder(reader).Decode(&wrapper); err != nil { - return Service{}, fmt.Errorf("could not decode service: %w", err) - } - return wrapper.Service, nil +type Service struct { + Name string `yaml:"name"` + Docker Docker `yaml:"docker"` + Chart string `yaml:"chart"` } func (s Service) Build(ctx context.Context, tag string) (string, error) { diff --git a/go.mod b/go.mod index 01654b3..b8a49f9 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,10 @@ module github.com/foomo/configurd go 1.14 require ( + github.com/go-delve/delve v1.4.0 // indirect github.com/otiai10/copy v1.0.2 github.com/spf13/cobra v0.0.6 - github.com/stretchr/testify v1.2.2 + github.com/stretchr/testify v1.3.0 + golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index f942c05..0f01249 100644 --- a/go.sum +++ b/go.sum @@ -13,7 +13,10 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= +github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -30,6 +33,8 @@ github.com/foomo/gorpc v0.0.0-20170320121619-aafa90603875/go.mod h1:0uzKetayf2X/ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-delve/delve v1.4.0 h1:O+1dw1XBZXqhC6fIPQwGxLlbd2wDRau7NxNhVpw02ag= +github.com/go-delve/delve v1.4.0/go.mod h1:gQM0ReOJLNAvPuKAXfjHngtE93C2yc/ekTbo7YbAHSo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -49,6 +54,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -64,6 +70,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -73,16 +81,22 @@ github.com/moby/moby v1.13.1 h1:mC5WwQwCXt/dYxZ1cIrRsnJAWw7VdtcTZUIGr4tXzOM= github.com/moby/moby v1.13.1/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -95,10 +109,13 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -106,63 +123,85 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/helm.go b/helm.go index 00076d9..925a7a9 100644 --- a/helm.go +++ b/helm.go @@ -7,16 +7,33 @@ import ( "path/filepath" "github.com/otiai10/copy" + "gopkg.in/yaml.v2" ) type ServiceDeployment struct { - Name string ServiceName string `yaml:"service"` Overrides interface{} } -func generate(deployment, outputDir, chartPath string) error { - dir := path.Join(outputDir, deployment) +func generateYaml(path string, data interface{}) error { + out, marshalErr := yaml.Marshal(data) + if marshalErr != nil { + return marshalErr + } + file, crateErr := os.Create(path) + if crateErr != nil { + return crateErr + } + defer file.Close() + _, writeErr := file.Write(out) + if writeErr != nil { + return writeErr + } + return nil +} + +func generate(sd ServiceDeployment, outputDir, chartPath, tag string) error { + dir := path.Join(outputDir, sd.ServiceName) if err := os.MkdirAll(dir, 0744); err != nil { return fmt.Errorf("could not create output dir: %w", err) } @@ -40,26 +57,43 @@ func generate(deployment, outputDir, chartPath string) error { return fmt.Errorf("could not copy template files: %w", err) } - //TODO: Write overrides into overrides.yaml (global) - + err = generateYaml(path.Join(dir, "overrides.yaml"), sd.Overrides) + if err != nil { + return fmt.Errorf("could not generate overrides.yaml: %w", err) + } return nil } -func (c Configurd) Deploy(namespace, deployment, tag string) (string, error) { - //TODO: Generate Output YAML file +func (c Configurd) Deploy(sd ServiceDeployment, namespace, tag string) (string, error) { output := "deployments" if err := os.RemoveAll(output); err != nil { - return "", fmt.Errorf("coudl not clean deployment directory: %w", err) + return "", fmt.Errorf("could not clean deployment directory: %w", err) } - if err := os.MkdirAll("deployment", 0744); err != nil { - return "", fmt.Errorf("could not create a deployment directory: %w", err) + if err := os.MkdirAll(output, 0744); err != nil { + return "", fmt.Errorf("could not create a deployments directory: %w", err) } - err := generate(deployment, output, "configurd/charts/example") + err := generate(sd, output, "configurd/charts/example", tag) if err != nil { return "", err } return "", nil } + +func (c Configurd) ServiceDeployments(baseDir, namespace, deployment string) ([]ServiceDeployment, error) { + file, err := os.Open(path.Join(baseDir, defaultNamespaceDir, namespace, deployment+defaultConfigFileExt)) + defer file.Close() + if err != nil { + return []ServiceDeployment{}, err + } + + var wrapper struct { + ServiceDeployments []ServiceDeployment `yaml:"services"` + } + if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { + return []ServiceDeployment{}, fmt.Errorf("could not decode service: %w", err) + } + return wrapper.ServiceDeployments, nil +} From 01162c11459fa9583b18b34540548c2abe5a255b Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 3 Mar 2020 11:25:50 +0100 Subject: [PATCH 06/18] Fixed service deployment issue. Moved generated charts into configurd --- cmd/actions/deploy.go | 4 +-- cmd/actions/root.go | 3 ++- configurd.go | 2 ++ example/{ => configurd}/.gitignore | 0 .../namespaces/local/hello-deployment.yml | 7 ++++- example/configurd/services/hi-service.yml | 8 ++++++ go.mod | 2 ++ go.sum | 3 +++ helm.go | 26 +++++++++++++------ 9 files changed, 42 insertions(+), 13 deletions(-) rename example/{ => configurd}/.gitignore (100%) create mode 100644 example/configurd/services/hi-service.yml diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index 774e663..13efd66 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -37,7 +37,5 @@ func Deploy(namespace, deployment, tag string) { Build(serviceDeployment.ServiceName, FlagTag) } } - for _, serviceDeployment := range sds { - cnf.Deploy(serviceDeployment, namespace, tag) - } + cnf.Deploy(sds, tag) } diff --git a/cmd/actions/root.go b/cmd/actions/root.go index 3452199..030633b 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -28,7 +28,8 @@ func init() { } func Execute() { - cwdir, err := os.Getwd() + var err error = nil + cwdir, err = os.Getwd() if err != nil { log.Fatal(err) } diff --git a/configurd.go b/configurd.go index dfc9cb0..010feb1 100644 --- a/configurd.go +++ b/configurd.go @@ -16,6 +16,8 @@ const ( defaultConfigFileExt = ".yml" defaultServiceDir = "configurd/services" defaultNamespaceDir = "configurd/namespaces" + defaultChartDir = "configurd/charts" + defaultDeploymentDir = "configurd/deployments" ) var ( diff --git a/example/.gitignore b/example/configurd/.gitignore similarity index 100% rename from example/.gitignore rename to example/configurd/.gitignore diff --git a/example/configurd/namespaces/local/hello-deployment.yml b/example/configurd/namespaces/local/hello-deployment.yml index b8a9edb..0169a7c 100644 --- a/example/configurd/namespaces/local/hello-deployment.yml +++ b/example/configurd/namespaces/local/hello-deployment.yml @@ -3,4 +3,9 @@ services: overrides: image: repository: foomo/configurd-hello - tag: hehe \ No newline at end of file + tag: hehe + - service: "hi-service" + overrides: + image: + repository: foomo/configurd-hello + tag: asd \ No newline at end of file diff --git a/example/configurd/services/hi-service.yml b/example/configurd/services/hi-service.yml new file mode 100644 index 0000000..da8f152 --- /dev/null +++ b/example/configurd/services/hi-service.yml @@ -0,0 +1,8 @@ +service: + name: hi-service + docker: + image: foomo/configurd-hello + file: Dockerfile + context: application + # options: + chart: example diff --git a/go.mod b/go.mod index b8a49f9..fe8a0fe 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,9 @@ module github.com/foomo/configurd go 1.14 require ( + github.com/davecgh/go-spew v1.1.1 github.com/go-delve/delve v1.4.0 // indirect + github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6 // indirect github.com/otiai10/copy v1.0.2 github.com/spf13/cobra v0.0.6 github.com/stretchr/testify v1.3.0 diff --git a/go.sum b/go.sum index 0f01249..ad3644c 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6 h1:iRn4qXDcqlb2sFqTdyTdYKjaPoLka7tvcyF+FZA9/qw= +github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6/go.mod h1:+tyhT8jBF8E0XvdlSXOSL7Iko7DlNiongHq3q+wcsPs= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -183,6 +185,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f h1:3MlESg/jvTr87F4ttA/q4B+uhe/q6qleC9/DP+IwQmY= golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/helm.go b/helm.go index 925a7a9..d9a8a4f 100644 --- a/helm.go +++ b/helm.go @@ -2,6 +2,7 @@ package configurd import ( "fmt" + "log" "os" "path" "path/filepath" @@ -13,6 +14,7 @@ import ( type ServiceDeployment struct { ServiceName string `yaml:"service"` Overrides interface{} + chart string } func generateYaml(path string, data interface{}) error { @@ -64,19 +66,19 @@ func generate(sd ServiceDeployment, outputDir, chartPath, tag string) error { return nil } -func (c Configurd) Deploy(sd ServiceDeployment, namespace, tag string) (string, error) { - output := "deployments" - if err := os.RemoveAll(output); err != nil { +func (c Configurd) Deploy(sds []ServiceDeployment, tag string) (string, error) { + if err := os.RemoveAll(defaultDeploymentDir); err != nil { return "", fmt.Errorf("could not clean deployment directory: %w", err) } - if err := os.MkdirAll(output, 0744); err != nil { + if err := os.MkdirAll(defaultDeploymentDir, 0744); err != nil { return "", fmt.Errorf("could not create a deployments directory: %w", err) } - - err := generate(sd, output, "configurd/charts/example", tag) - if err != nil { - return "", err + for _, sd := range sds { + err := generate(sd, defaultDeploymentDir, path.Join(defaultChartDir, sd.chart), tag) + if err != nil { + return "", err + } } return "", nil @@ -95,5 +97,13 @@ func (c Configurd) ServiceDeployments(baseDir, namespace, deployment string) ([] if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { return []ServiceDeployment{}, fmt.Errorf("could not decode service: %w", err) } + + for i, sd := range wrapper.ServiceDeployments { + svc, err := c.Service(sd.ServiceName) + if err != nil { + log.Fatal(err) + } + wrapper.ServiceDeployments[i].chart = svc.Chart + } return wrapper.ServiceDeployments, nil } From 657b0c236927dfd7cd28ac6333a4e6f9432f7266 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 3 Mar 2020 13:10:35 +0100 Subject: [PATCH 07/18] Add helm install to deploy --- cmd/actions/deploy.go | 2 +- configurd.go | 1 + go.mod | 2 -- go.sum | 1 + helm.go | 28 +++++++++++++++++++++++++--- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index 13efd66..b65f6d6 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -37,5 +37,5 @@ func Deploy(namespace, deployment, tag string) { Build(serviceDeployment.ServiceName, FlagTag) } } - cnf.Deploy(sds, tag) + cnf.Deploy(sds, cwdir, tag) } diff --git a/configurd.go b/configurd.go index 010feb1..162d839 100644 --- a/configurd.go +++ b/configurd.go @@ -18,6 +18,7 @@ const ( defaultNamespaceDir = "configurd/namespaces" defaultChartDir = "configurd/charts" defaultDeploymentDir = "configurd/deployments" + defaultOverridesFile = "overrides.yaml" ) var ( diff --git a/go.mod b/go.mod index fe8a0fe..7b2e248 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,10 @@ module github.com/foomo/configurd go 1.14 require ( - github.com/davecgh/go-spew v1.1.1 github.com/go-delve/delve v1.4.0 // indirect github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6 // indirect github.com/otiai10/copy v1.0.2 github.com/spf13/cobra v0.0.6 github.com/stretchr/testify v1.3.0 - golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index ad3644c..6fd2749 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2i github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 h1:+OLn68pqasWca0z5ryit9KGfp3sUsW4Lqg32iRMJyzs= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= diff --git a/helm.go b/helm.go index d9a8a4f..b793320 100644 --- a/helm.go +++ b/helm.go @@ -4,8 +4,10 @@ import ( "fmt" "log" "os" + "os/exec" "path" "path/filepath" + "strings" "github.com/otiai10/copy" "gopkg.in/yaml.v2" @@ -15,6 +17,7 @@ type ServiceDeployment struct { ServiceName string `yaml:"service"` Overrides interface{} chart string + namespace string } func generateYaml(path string, data interface{}) error { @@ -59,14 +62,14 @@ func generate(sd ServiceDeployment, outputDir, chartPath, tag string) error { return fmt.Errorf("could not copy template files: %w", err) } - err = generateYaml(path.Join(dir, "overrides.yaml"), sd.Overrides) + err = generateYaml(path.Join(dir, defaultOverridesFile), sd.Overrides) if err != nil { - return fmt.Errorf("could not generate overrides.yaml: %w", err) + return fmt.Errorf("could not generate %v: %w", defaultOverridesFile, err) } return nil } -func (c Configurd) Deploy(sds []ServiceDeployment, tag string) (string, error) { +func (c Configurd) Deploy(sds []ServiceDeployment, basePath, tag string) (string, error) { if err := os.RemoveAll(defaultDeploymentDir); err != nil { return "", fmt.Errorf("could not clean deployment directory: %w", err) } @@ -79,6 +82,24 @@ func (c Configurd) Deploy(sds []ServiceDeployment, tag string) (string, error) { if err != nil { return "", err } + + // helm install + chartPath := path.Join(basePath, defaultDeploymentDir, sd.ServiceName) + cmdArgs := []string{ + "install", sd.ServiceName, chartPath, + "-f", path.Join(chartPath, defaultOverridesFile), + "-n", sd.namespace, + "--set", "fullnameOverride=" + sd.ServiceName, + } + cmd := exec.Command("helm", cmdArgs...) + + out, err := cmd.CombinedOutput() + output := strings.Replace(string(out), "\n", "\n\t", -1) + + log.Print(string(out)) + if err != nil { + return "", fmt.Errorf("could not install a helm chart for service %v output: \n%v", sd.ServiceName, output) + } } return "", nil @@ -104,6 +125,7 @@ func (c Configurd) ServiceDeployments(baseDir, namespace, deployment string) ([] log.Fatal(err) } wrapper.ServiceDeployments[i].chart = svc.Chart + wrapper.ServiceDeployments[i].namespace = namespace } return wrapper.ServiceDeployments, nil } From 8ef2d75fcb53f2cbfeac89cda4814af6bc88fb96 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 3 Mar 2020 15:23:10 +0100 Subject: [PATCH 08/18] Refactor service deployment loading logic --- cmd/actions/deploy.go | 10 ++-- configurd.go | 112 +++++++++++++++++++++++++++++++++++++++++- helm.go | 72 ++++++++++----------------- 3 files changed, 143 insertions(+), 51 deletions(-) diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index b65f6d6..b55718b 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -28,14 +28,14 @@ var ( ) func Deploy(namespace, deployment, tag string) { - sds, err := cnf.ServiceDeployments(cwdir, namespace, deployment) - if err != nil { - log.Fatalf("could not deploy: %v output:\n%v", deployment, err) + sds := cnf.GetServiceDeployments(namespace, deployment) + if len(sds) == 0 { + log.Fatalf("could not find any service deployments for namespace: %v and deployment: %v", namespace, deployment) } if flagBuild { for _, serviceDeployment := range sds { - Build(serviceDeployment.ServiceName, FlagTag) + Build(serviceDeployment.ServiceName, tag) } } - cnf.Deploy(sds, cwdir, tag) + cnf.Deploy(sds, cwdir) } diff --git a/configurd.go b/configurd.go index 162d839..b9b0d34 100644 --- a/configurd.go +++ b/configurd.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "log" "os" "path" "path/filepath" @@ -25,10 +26,20 @@ var ( ErrServiceNotFound = errors.New("service not found") ) +type Deployment struct { + name string + serviceDeployments []ServiceDeployment +} + +type Namespace struct { + name string + deployments []Deployment +} + type Configurd struct { Services []Service Templates []string - Namespaces []string + Namespaces []Namespace } func New(dir string) (Configurd, error) { @@ -59,9 +70,88 @@ func New(dir string) (Configurd, error) { return Configurd{}, err } + c.Namespaces, err = loadNamespaces(c, dir) + + if err != nil { + return Configurd{}, err + } + return c, nil } +func loadNamespaces(c Configurd, basePath string) ([]Namespace, error) { + var nss []Namespace + namespaceDir := path.Join(basePath, defaultNamespaceDir) + err := filepath.Walk(namespaceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() && path != namespaceDir { + ds, err := loadDeployments(c, info.Name()) + if err != nil { + return err + } + ns := Namespace{ + name: info.Name(), + deployments: ds, + } + nss = append(nss, ns) + } + return nil + }) + return nss, err +} + +func loadDeployments(c Configurd, namespace string) ([]Deployment, error) { + var ds []Deployment + err := filepath.Walk(path.Join(defaultNamespaceDir, namespace), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { + var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] + sds, err := loadServiceDeployments(c, namespace, name) + if err != nil { + return err + } + d := Deployment{ + name: name, + serviceDeployments: sds, + } + ds = append(ds, d) + } + return nil + }) + return ds, err +} + +func loadServiceDeployments(c Configurd, namespace, deployment string) ([]ServiceDeployment, error) { + path := path.Join(defaultNamespaceDir, namespace, deployment+defaultConfigFileExt) + file, err := os.Open(path) + defer file.Close() + if err != nil { + return []ServiceDeployment{}, err + } + + var wrapper struct { + ServiceDeployments []ServiceDeployment `yaml:"services"` + } + if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { + return []ServiceDeployment{}, fmt.Errorf("could not decode service deployment: %w", err) + } + + for i, sd := range wrapper.ServiceDeployments { + svc, err := c.Service(sd.ServiceName) + if err != nil { + log.Fatal(err) + } + wrapper.ServiceDeployments[i].chart = svc.Chart + wrapper.ServiceDeployments[i].namespace = namespace + wrapper.ServiceDeployments[i].deployment = deployment + } + return wrapper.ServiceDeployments, nil +} + func (c Configurd) Service(name string) (Service, error) { for _, svc := range c.Services { if svc.Name == name { @@ -71,6 +161,26 @@ func (c Configurd) Service(name string) (Service, error) { return Service{}, ErrServiceNotFound } +func (c Configurd) GetServiceDeployments(namespace, deployment string) []ServiceDeployment { + var sds []ServiceDeployment + for _, sd := range c.serviceDeployments() { + if sd.namespace == namespace && sd.deployment == deployment { + sds = append(sds, sd) + } + } + return sds +} + +func (c Configurd) serviceDeployments() []ServiceDeployment { + var sds []ServiceDeployment + for _, ns := range c.Namespaces { + for _, d := range ns.deployments { + sds = append(sds, d.serviceDeployments...) + } + } + return sds +} + func loadService(reader io.Reader) (Service, error) { var wrapper struct { Service Service `yaml:"service"` diff --git a/helm.go b/helm.go index b793320..d317e72 100644 --- a/helm.go +++ b/helm.go @@ -16,8 +16,9 @@ import ( type ServiceDeployment struct { ServiceName string `yaml:"service"` Overrides interface{} - chart string namespace string + deployment string + chart string } func generateYaml(path string, data interface{}) error { @@ -37,7 +38,7 @@ func generateYaml(path string, data interface{}) error { return nil } -func generate(sd ServiceDeployment, outputDir, chartPath, tag string) error { +func generate(sd ServiceDeployment, outputDir, chartPath string) error { dir := path.Join(outputDir, sd.ServiceName) if err := os.MkdirAll(dir, 0744); err != nil { return fmt.Errorf("could not create output dir: %w", err) @@ -69,7 +70,27 @@ func generate(sd ServiceDeployment, outputDir, chartPath, tag string) error { return nil } -func (c Configurd) Deploy(sds []ServiceDeployment, basePath, tag string) (string, error) { +func helmInstall(serviceName, namespace, basePath string) error { + chartPath := path.Join(basePath, defaultDeploymentDir, serviceName) + cmdArgs := []string{ + "install", serviceName, chartPath, + "-f", path.Join(chartPath, defaultOverridesFile), + "-n", namespace, + "--set", "fullnameOverride=" + serviceName, + } + cmd := exec.Command("helm", cmdArgs...) + + out, err := cmd.CombinedOutput() + output := strings.Replace(string(out), "\n", "\n\t", -1) + + log.Print(string(out)) + if err != nil { + return fmt.Errorf("could not install a helm chart for service %v output: \n%v", serviceName, output) + } + return nil +} + +func (c Configurd) Deploy(sds []ServiceDeployment, basePath string) (string, error) { if err := os.RemoveAll(defaultDeploymentDir); err != nil { return "", fmt.Errorf("could not clean deployment directory: %w", err) } @@ -78,54 +99,15 @@ func (c Configurd) Deploy(sds []ServiceDeployment, basePath, tag string) (string return "", fmt.Errorf("could not create a deployments directory: %w", err) } for _, sd := range sds { - err := generate(sd, defaultDeploymentDir, path.Join(defaultChartDir, sd.chart), tag) + err := generate(sd, defaultDeploymentDir, path.Join(defaultChartDir, sd.chart)) if err != nil { return "", err } - - // helm install - chartPath := path.Join(basePath, defaultDeploymentDir, sd.ServiceName) - cmdArgs := []string{ - "install", sd.ServiceName, chartPath, - "-f", path.Join(chartPath, defaultOverridesFile), - "-n", sd.namespace, - "--set", "fullnameOverride=" + sd.ServiceName, - } - cmd := exec.Command("helm", cmdArgs...) - - out, err := cmd.CombinedOutput() - output := strings.Replace(string(out), "\n", "\n\t", -1) - - log.Print(string(out)) + err = helmInstall(sd.ServiceName, sd.namespace, basePath) if err != nil { - return "", fmt.Errorf("could not install a helm chart for service %v output: \n%v", sd.ServiceName, output) + return "", err } } return "", nil } - -func (c Configurd) ServiceDeployments(baseDir, namespace, deployment string) ([]ServiceDeployment, error) { - file, err := os.Open(path.Join(baseDir, defaultNamespaceDir, namespace, deployment+defaultConfigFileExt)) - defer file.Close() - if err != nil { - return []ServiceDeployment{}, err - } - - var wrapper struct { - ServiceDeployments []ServiceDeployment `yaml:"services"` - } - if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { - return []ServiceDeployment{}, fmt.Errorf("could not decode service: %w", err) - } - - for i, sd := range wrapper.ServiceDeployments { - svc, err := c.Service(sd.ServiceName) - if err != nil { - log.Fatal(err) - } - wrapper.ServiceDeployments[i].chart = svc.Chart - wrapper.ServiceDeployments[i].namespace = namespace - } - return wrapper.ServiceDeployments, nil -} From b71b621dfacf862f7e25d006869f721e532e28d3 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 3 Mar 2020 16:29:38 +0100 Subject: [PATCH 09/18] Add undeploy command and helm implementation --- cmd/actions/undeploy.go | 39 +++++++++++++++++++++++++++++++++++++++ configurd.go | 2 +- example/Makefile | 5 ++++- helm.go | 19 ++++++++++++++++++- 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 cmd/actions/undeploy.go diff --git a/cmd/actions/undeploy.go b/cmd/actions/undeploy.go new file mode 100644 index 0000000..e469499 --- /dev/null +++ b/cmd/actions/undeploy.go @@ -0,0 +1,39 @@ +package actions + +import ( + "log" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(undeployCmd) + undeployCmd.Flags().StringVarP(&flagDeployment, "deployment", "d", "", "Specifies the deployment name") +} + +var ( + flagDeployment string +) + +var ( + undeployCmd = &cobra.Command{ + Use: "undeploy [NAMESPACE] -d {DEPLOYMENT}", + Short: "Uneploys the specified deployment with given tag version", + Long: `Uneploys the specified deployment with given tag version`, + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + Undeploy(args[0], flagDeployment) + }, + } +) + +func Undeploy(namespace, deployment string) { + sds := cnf.GetServiceDeployments(namespace, deployment) + if len(sds) == 0 { + log.Fatalf("could not find any service deployments for namespace: %v and deployment: %v", namespace, deployment) + } + err := cnf.Undeploy(sds) + if err != nil { + log.Fatal(err) + } +} diff --git a/configurd.go b/configurd.go index b9b0d34..be34905 100644 --- a/configurd.go +++ b/configurd.go @@ -164,7 +164,7 @@ func (c Configurd) Service(name string) (Service, error) { func (c Configurd) GetServiceDeployments(namespace, deployment string) []ServiceDeployment { var sds []ServiceDeployment for _, sd := range c.serviceDeployments() { - if sd.namespace == namespace && sd.deployment == deployment { + if sd.namespace == namespace && (sd.deployment == deployment || deployment == "") { sds = append(sds, sd) } } diff --git a/example/Makefile b/example/Makefile index 12039da..6008116 100644 --- a/example/Makefile +++ b/example/Makefile @@ -2,4 +2,7 @@ build: go run ../cmd/main.go build hello-service -t master deploy: - go run ../cmd/main.go deploy local hello-deployment -t master \ No newline at end of file + go run ../cmd/main.go deploy local hello-deployment -t master + +undeploy: + go run ../cmd/main.go undeploy local -d hello-deployment diff --git a/helm.go b/helm.go index d317e72..08558d6 100644 --- a/helm.go +++ b/helm.go @@ -83,7 +83,6 @@ func helmInstall(serviceName, namespace, basePath string) error { out, err := cmd.CombinedOutput() output := strings.Replace(string(out), "\n", "\n\t", -1) - log.Print(string(out)) if err != nil { return fmt.Errorf("could not install a helm chart for service %v output: \n%v", serviceName, output) } @@ -111,3 +110,21 @@ func (c Configurd) Deploy(sds []ServiceDeployment, basePath string) (string, err return "", nil } + +func (c Configurd) Undeploy(sds []ServiceDeployment) error { + for _, sd := range sds { + cmdArgs := []string{ + "uninstall", "-n", sd.namespace, sd.ServiceName, + } + cmd := exec.Command("helm", cmdArgs...) + + out, err := cmd.CombinedOutput() + output := strings.Replace(string(out), "\n", "\n\t", -1) + + if err != nil { + return fmt.Errorf("could not uninstall a helm chart for namespace: %v, deployment: %v, output: \n%v", sd.namespace, sd.deployment, output) + } + log.Print(string(out)) + } + return nil +} From 47fdce041994e8da68cb21d367757e140b76386f Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 3 Mar 2020 16:33:49 +0100 Subject: [PATCH 10/18] Refactor deploy and undeploy --- cmd/actions/deploy.go | 5 ++++- helm.go | 30 +++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index b55718b..2c14462 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -37,5 +37,8 @@ func Deploy(namespace, deployment, tag string) { Build(serviceDeployment.ServiceName, tag) } } - cnf.Deploy(sds, cwdir) + err := cnf.Deploy(sds, cwdir) + if err != nil { + log.Fatal(err) + } } diff --git a/helm.go b/helm.go index 08558d6..6c54cb2 100644 --- a/helm.go +++ b/helm.go @@ -86,29 +86,45 @@ func helmInstall(serviceName, namespace, basePath string) error { if err != nil { return fmt.Errorf("could not install a helm chart for service %v output: \n%v", serviceName, output) } + log.Print(string(out)) return nil } -func (c Configurd) Deploy(sds []ServiceDeployment, basePath string) (string, error) { +func helmUninstall(sd ServiceDeployment) error { + cmdArgs := []string{ + "uninstall", "-n", sd.namespace, sd.ServiceName, + } + cmd := exec.Command("helm", cmdArgs...) + + out, err := cmd.CombinedOutput() + output := strings.Replace(string(out), "\n", "\n\t", -1) + + if err != nil { + return fmt.Errorf("could not uninstall a helm chart for namespace: %v, deployment: %v, output: \n%v", sd.namespace, sd.deployment, output) + } + log.Print(string(out)) + return nil +} + +func (c Configurd) Deploy(sds []ServiceDeployment, basePath string) error { if err := os.RemoveAll(defaultDeploymentDir); err != nil { - return "", fmt.Errorf("could not clean deployment directory: %w", err) + return fmt.Errorf("could not clean deployment directory: %w", err) } if err := os.MkdirAll(defaultDeploymentDir, 0744); err != nil { - return "", fmt.Errorf("could not create a deployments directory: %w", err) + return fmt.Errorf("could not create a deployments directory: %w", err) } for _, sd := range sds { err := generate(sd, defaultDeploymentDir, path.Join(defaultChartDir, sd.chart)) if err != nil { - return "", err + return err } err = helmInstall(sd.ServiceName, sd.namespace, basePath) if err != nil { - return "", err + return err } } - - return "", nil + return nil } func (c Configurd) Undeploy(sds []ServiceDeployment) error { From 11341594d578973cd3b012c2f8f2d872b07f5bde Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Mon, 9 Mar 2020 13:56:42 +0100 Subject: [PATCH 11/18] Refactor commands and configurations --- cmd/actions/build.go | 8 +- cmd/actions/deploy.go | 26 ++--- cmd/actions/undeploy.go | 20 ++-- configurd.go | 97 +++++++++-------- docker.go | 29 ++--- example/application/hello.go | 10 +- example/configurd/.gitignore | 2 +- .../charts/example/templates/deployment.yaml | 24 +++-- .../charts/example/templates/ingress.yaml | 3 +- .../charts/example/templates/service.yaml | 2 +- example/configurd/charts/example/values.yaml | 7 +- .../namespaces/local/hello-deployment.yml | 11 -- .../configurd/namespaces/local/hello-sg.yml | 9 ++ example/configurd/services/hello-service.yml | 7 +- example/configurd/services/hi-service.yml | 9 +- helm.go | 102 ++++++++++-------- 16 files changed, 193 insertions(+), 173 deletions(-) delete mode 100644 example/configurd/namespaces/local/hello-deployment.yml create mode 100644 example/configurd/namespaces/local/hello-sg.yml diff --git a/cmd/actions/build.go b/cmd/actions/build.go index f97d349..039598d 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -1,15 +1,14 @@ package actions import ( - "context" "log" "github.com/spf13/cobra" ) var cmdBuild = &cobra.Command{ - Use: "build [configuration file to use for service group] -t TAG", - Short: "Add a service group", + Use: "build [configuration file to use for service] -t TAG", + Short: "Build a service", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { Build(args[0], cmd.Flag("tag").Value.String()) @@ -21,12 +20,11 @@ func init() { } func Build(service, tag string) { - svc, err := cnf.Service(service) if err != nil { log.Fatalf("service not found: %v", err) } - output, err := svc.Build(context.Background(), tag) + output, err := svc.RunBuild(tag) if err != nil { log.Fatalf("could not build: %v output:\n%v", output, err) } diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go index 2c14462..0baf0d7 100644 --- a/cmd/actions/deploy.go +++ b/cmd/actions/deploy.go @@ -8,18 +8,20 @@ import ( func init() { rootCmd.AddCommand(deployCmd) - deployCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build deployment before publishing") + deployCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build service group before publishing") + deployCmd.Flags().StringVarP(&flagOutputDir, "output", "o", "default", "Specifies output directory") } var ( - flagBuild bool + flagBuild bool + flagOutputDir string ) var ( deployCmd = &cobra.Command{ - Use: "deploy [NAMESPACE] [DEPLOYMENT] -t {TAG}", - Short: "Deploys the specified deployment with given tag version", - Long: `Deploys the specified deployment with given tag version`, + Use: "deploy [NAMESPACE] [SERVICE GROUP] -t {TAG}", + Short: "Deploys the specified service group with given tag version", + Long: `Deploys the specified service group with given tag version`, Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { Deploy(args[0], args[1], FlagTag) @@ -27,17 +29,17 @@ var ( } ) -func Deploy(namespace, deployment, tag string) { - sds := cnf.GetServiceDeployments(namespace, deployment) - if len(sds) == 0 { - log.Fatalf("could not find any service deployments for namespace: %v and deployment: %v", namespace, deployment) +func Deploy(namespace, serviceGroup, tag string) { + sgis := cnf.GetServiceGroupItems(namespace, serviceGroup) + if len(sgis) == 0 { + log.Fatalf("could not find any service for namespace: %v and service group: %v", namespace, serviceGroup) } if flagBuild { - for _, serviceDeployment := range sds { - Build(serviceDeployment.ServiceName, tag) + for _, sgi := range sgis { + Build(sgi.ServiceName, tag) } } - err := cnf.Deploy(sds, cwdir) + err := cnf.Deploy(sgis, flagOutputDir, tag) if err != nil { log.Fatal(err) } diff --git a/cmd/actions/undeploy.go b/cmd/actions/undeploy.go index e469499..070b2ca 100644 --- a/cmd/actions/undeploy.go +++ b/cmd/actions/undeploy.go @@ -8,31 +8,31 @@ import ( func init() { rootCmd.AddCommand(undeployCmd) - undeployCmd.Flags().StringVarP(&flagDeployment, "deployment", "d", "", "Specifies the deployment name") + undeployCmd.Flags().StringVarP(&FlagSG, "sg", "g", "", "Specifies the service group name") } var ( - flagDeployment string + FlagSG string ) var ( undeployCmd = &cobra.Command{ - Use: "undeploy [NAMESPACE] -d {DEPLOYMENT}", - Short: "Uneploys the specified deployment with given tag version", - Long: `Uneploys the specified deployment with given tag version`, + Use: "undeploy [NAMESPACE] -g {SERVICE GROUP}", + Short: "Uneploys the specified service group with given tag version", + Long: `Uneploys the specified service group with given tag version`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - Undeploy(args[0], flagDeployment) + Undeploy(args[0], FlagSG) }, } ) func Undeploy(namespace, deployment string) { - sds := cnf.GetServiceDeployments(namespace, deployment) - if len(sds) == 0 { - log.Fatalf("could not find any service deployments for namespace: %v and deployment: %v", namespace, deployment) + sgis := cnf.GetServiceGroupItems(namespace, deployment) + if len(sgis) == 0 { + log.Fatalf("could not find any service for namespace: %v and service group: %v", namespace, deployment) } - err := cnf.Undeploy(sds) + err := cnf.Undeploy(sgis) if err != nil { log.Fatal(err) } diff --git a/configurd.go b/configurd.go index be34905..4296f4b 100644 --- a/configurd.go +++ b/configurd.go @@ -18,7 +18,7 @@ const ( defaultServiceDir = "configurd/services" defaultNamespaceDir = "configurd/namespaces" defaultChartDir = "configurd/charts" - defaultDeploymentDir = "configurd/deployments" + defaultOutputDir = "configurd/.workdir" defaultOverridesFile = "overrides.yaml" ) @@ -26,14 +26,14 @@ var ( ErrServiceNotFound = errors.New("service not found") ) -type Deployment struct { - name string - serviceDeployments []ServiceDeployment +type ServiceGroup struct { + name string + items []ServiceGroupItem } type Namespace struct { - name string - deployments []Deployment + name string + serviceGroups []ServiceGroup } type Configurd struct { @@ -43,9 +43,9 @@ type Configurd struct { } func New(dir string) (Configurd, error) { - c := Configurd{} + log.Printf("Parsing configuration files") - //load services + c := Configurd{} serviceDir := path.Join(dir, defaultServiceDir) err := filepath.Walk(serviceDir, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -57,7 +57,10 @@ func New(dir string) (Configurd, error) { return err } defer file.Close() - svc, err := loadService(file) + + var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] + log.Printf("Loading service: %v, from: %v", name, path) + svc, err := loadService(file, name) if err != nil { return err } @@ -87,13 +90,14 @@ func loadNamespaces(c Configurd, basePath string) ([]Namespace, error) { return err } if info.IsDir() && path != namespaceDir { - ds, err := loadDeployments(c, info.Name()) + log.Printf("Loading namespace: %v, from: %v", info.Name(), path) + sgs, err := loadServiceGroups(c, info.Name()) if err != nil { return err } ns := Namespace{ - name: info.Name(), - deployments: ds, + name: info.Name(), + serviceGroups: sgs, } nss = append(nss, ns) } @@ -102,54 +106,58 @@ func loadNamespaces(c Configurd, basePath string) ([]Namespace, error) { return nss, err } -func loadDeployments(c Configurd, namespace string) ([]Deployment, error) { - var ds []Deployment - err := filepath.Walk(path.Join(defaultNamespaceDir, namespace), func(path string, info os.FileInfo, err error) error { +func loadServiceGroups(c Configurd, namespace string) ([]ServiceGroup, error) { + var sgs []ServiceGroup + + sgDir := path.Join(defaultNamespaceDir, namespace) + err := filepath.Walk(sgDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] - sds, err := loadServiceDeployments(c, namespace, name) + log.Printf("Loading service group: %v, from: %v", name, path) + sgis, err := loadServiceGroupItems(c, namespace, name) if err != nil { return err } - d := Deployment{ - name: name, - serviceDeployments: sds, + sg := ServiceGroup{ + name: name, + items: sgis, } - ds = append(ds, d) + sgs = append(sgs, sg) } return nil }) - return ds, err + return sgs, err } -func loadServiceDeployments(c Configurd, namespace, deployment string) ([]ServiceDeployment, error) { - path := path.Join(defaultNamespaceDir, namespace, deployment+defaultConfigFileExt) +func loadServiceGroupItems(c Configurd, namespace, serviceGroup string) ([]ServiceGroupItem, error) { + path := path.Join(defaultNamespaceDir, namespace, serviceGroup+defaultConfigFileExt) file, err := os.Open(path) defer file.Close() if err != nil { - return []ServiceDeployment{}, err + return []ServiceGroupItem{}, err } var wrapper struct { - ServiceDeployments []ServiceDeployment `yaml:"services"` + Items []ServiceGroupItem `yaml:"serviceGroup"` } if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { - return []ServiceDeployment{}, fmt.Errorf("could not decode service deployment: %w", err) + return []ServiceGroupItem{}, fmt.Errorf("could not decode service group items: %w", err) } - for i, sd := range wrapper.ServiceDeployments { - svc, err := c.Service(sd.ServiceName) + for i, sgi := range wrapper.Items { + log.Printf("Loading service group item: %v", sgi.ServiceName) + svc, err := c.Service(sgi.ServiceName) if err != nil { log.Fatal(err) } - wrapper.ServiceDeployments[i].chart = svc.Chart - wrapper.ServiceDeployments[i].namespace = namespace - wrapper.ServiceDeployments[i].deployment = deployment + wrapper.Items[i].chart = svc.Chart + wrapper.Items[i].namespace = namespace + wrapper.Items[i].serviceGroup = serviceGroup } - return wrapper.ServiceDeployments, nil + return wrapper.Items, nil } func (c Configurd) Service(name string) (Service, error) { @@ -161,32 +169,33 @@ func (c Configurd) Service(name string) (Service, error) { return Service{}, ErrServiceNotFound } -func (c Configurd) GetServiceDeployments(namespace, deployment string) []ServiceDeployment { - var sds []ServiceDeployment - for _, sd := range c.serviceDeployments() { - if sd.namespace == namespace && (sd.deployment == deployment || deployment == "") { - sds = append(sds, sd) +func (c Configurd) GetServiceGroupItems(namespace, serviceGroup string) []ServiceGroupItem { + var sgis []ServiceGroupItem + for _, sgi := range c.serviceGroupItems() { + if sgi.namespace == namespace && (sgi.serviceGroup == serviceGroup || serviceGroup == "") { + sgis = append(sgis, sgi) } } - return sds + return sgis } -func (c Configurd) serviceDeployments() []ServiceDeployment { - var sds []ServiceDeployment +func (c Configurd) serviceGroupItems() []ServiceGroupItem { + var sgis []ServiceGroupItem for _, ns := range c.Namespaces { - for _, d := range ns.deployments { - sds = append(sds, d.serviceDeployments...) + for _, sg := range ns.serviceGroups { + sgis = append(sgis, sg.items...) } } - return sds + return sgis } -func loadService(reader io.Reader) (Service, error) { +func loadService(reader io.Reader, name string) (Service, error) { var wrapper struct { Service Service `yaml:"service"` } if err := yaml.NewDecoder(reader).Decode(&wrapper); err != nil { return Service{}, fmt.Errorf("could not decode service: %w", err) } + wrapper.Service.Name = name return wrapper.Service, nil } diff --git a/docker.go b/docker.go index 463359b..256db1b 100644 --- a/docker.go +++ b/docker.go @@ -1,35 +1,28 @@ package configurd import ( - "context" - "fmt" + "log" "os/exec" "strings" ) -type Docker struct { - File string `yaml:"file"` - Context string `yaml:"context"` - Options string `yaml:"options"` +type Build struct { + Command string `yaml:"command"` Image string `yaml:"image"` } type Service struct { - Name string `yaml:"name"` - Docker Docker `yaml:"docker"` - Chart string `yaml:"chart"` + Name string + Build Build `yaml:"build"` + Chart string `yaml:"chart"` } -func (s Service) Build(ctx context.Context, tag string) (string, error) { - args := []string{ - "build", - "-f", s.Docker.File, - "-t", fmt.Sprintf("%s:%s", s.Docker.Image, tag), - ".", - } +func (s Service) RunBuild(tag string) (string, error) { + args := append(strings.Split(s.Build.Command, " "), "-t", s.Build.Image+":"+tag) + log.Printf("Running command: %v", strings.Join(args, " ")) - cmd := exec.Command("docker", args...) - cmd.Dir = s.Docker.Context + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = defaultServiceDir out, err := cmd.CombinedOutput() output := strings.Replace(string(out), "\n", "\n\t", -1) diff --git a/example/application/hello.go b/example/application/hello.go index b01ae89..31bb0cb 100644 --- a/example/application/hello.go +++ b/example/application/hello.go @@ -7,13 +7,15 @@ import ( ) func main() { - address := flag.String("address", ":80", "address to listen to ") - + flagAddress := flag.String("address", ":80", "address to listen to ") + flagGreeting := flag.String("greeting", "HELLO", "sets the greeting message") flag.Parse() + greeting := []byte(*flagGreeting) + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte("HELLO")) + _, _ = w.Write(greeting) }) - log.Fatal(http.ListenAndServe(*address, handler)) + log.Fatal(http.ListenAndServe(*flagAddress, handler)) } diff --git a/example/configurd/.gitignore b/example/configurd/.gitignore index e6bc508..0cf391e 100644 --- a/example/configurd/.gitignore +++ b/example/configurd/.gitignore @@ -1 +1 @@ -deployments \ No newline at end of file +.workdir \ No newline at end of file diff --git a/example/configurd/charts/example/templates/deployment.yaml b/example/configurd/charts/example/templates/deployment.yaml index 028cb70..b3f60ab 100644 --- a/example/configurd/charts/example/templates/deployment.yaml +++ b/example/configurd/charts/example/templates/deployment.yaml @@ -20,21 +20,27 @@ spec: app.kubernetes.io/instance: {{ .Release.Name }} spec: containers: - - name: {{ .Chart.Name }} + - name: {{ .Values.nameOverride }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.args }} + args: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.env }} + env: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - - name: http - containerPort: 80 + - name: container-port + containerPort: {{ .Values.service.targetPort }} protocol: TCP livenessProbe: - httpGet: - path: / - port: http + tcpSocket: + port: container-port readinessProbe: - httpGet: - path: / - port: http + tcpSocket: + port: container-port resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/example/configurd/charts/example/templates/ingress.yaml b/example/configurd/charts/example/templates/ingress.yaml index bfb9fc3..71c2324 100644 --- a/example/configurd/charts/example/templates/ingress.yaml +++ b/example/configurd/charts/example/templates/ingress.yaml @@ -1,6 +1,7 @@ {{- if .Values.ingress.enabled -}} {{- $fullName := include "example.fullname" . -}} {{- $ingressPaths := .Values.ingress.paths -}} +{{- $servicePort := .Values.service.port -}} apiVersion: extensions/v1beta1 kind: Ingress metadata: @@ -34,7 +35,7 @@ spec: - path: {{ . }} backend: serviceName: {{ $fullName }} - servicePort: http + servicePort: {{ $servicePort }} {{- end }} {{- end }} {{- end }} diff --git a/example/configurd/charts/example/templates/service.yaml b/example/configurd/charts/example/templates/service.yaml index 7528fee..ad07067 100644 --- a/example/configurd/charts/example/templates/service.yaml +++ b/example/configurd/charts/example/templates/service.yaml @@ -11,7 +11,7 @@ spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} - targetPort: http + targetPort: {{ .Values.service.targetPort }} protocol: TCP name: http selector: diff --git a/example/configurd/charts/example/values.yaml b/example/configurd/charts/example/values.yaml index 04cf993..7eed295 100644 --- a/example/configurd/charts/example/values.yaml +++ b/example/configurd/charts/example/values.yaml @@ -14,10 +14,13 @@ fullnameOverride: "example" service: type: NodePort - port: 8080 + port: 80 + targetPort: 3000 + +env: [] ingress: - enabled: false + enabled: true annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" diff --git a/example/configurd/namespaces/local/hello-deployment.yml b/example/configurd/namespaces/local/hello-deployment.yml deleted file mode 100644 index 0169a7c..0000000 --- a/example/configurd/namespaces/local/hello-deployment.yml +++ /dev/null @@ -1,11 +0,0 @@ -services: - - service: "hello-service" - overrides: - image: - repository: foomo/configurd-hello - tag: hehe - - service: "hi-service" - overrides: - image: - repository: foomo/configurd-hello - tag: asd \ No newline at end of file diff --git a/example/configurd/namespaces/local/hello-sg.yml b/example/configurd/namespaces/local/hello-sg.yml new file mode 100644 index 0000000..b135e73 --- /dev/null +++ b/example/configurd/namespaces/local/hello-sg.yml @@ -0,0 +1,9 @@ +serviceGroup: + - service: "hello-service" + overrides: + args: + - "--greeting=hehe" + - service: "hi-service" + overrides: + args: + - "--greeting=hai" \ No newline at end of file diff --git a/example/configurd/services/hello-service.yml b/example/configurd/services/hello-service.yml index 7e7051f..2284653 100644 --- a/example/configurd/services/hello-service.yml +++ b/example/configurd/services/hello-service.yml @@ -1,8 +1,5 @@ service: - name: hello-service - docker: + build: + command: "docker build ../../application/" image: foomo/configurd-hello - file: Dockerfile - context: application - # options: chart: example diff --git a/example/configurd/services/hi-service.yml b/example/configurd/services/hi-service.yml index da8f152..01e5372 100644 --- a/example/configurd/services/hi-service.yml +++ b/example/configurd/services/hi-service.yml @@ -1,8 +1,5 @@ service: - name: hi-service - docker: - image: foomo/configurd-hello - file: Dockerfile - context: application - # options: + build: + command: "docker build ../../application/" + image: foomo/configurd-hi chart: example diff --git a/helm.go b/helm.go index 6c54cb2..8eecde5 100644 --- a/helm.go +++ b/helm.go @@ -13,15 +13,16 @@ import ( "gopkg.in/yaml.v2" ) -type ServiceDeployment struct { - ServiceName string `yaml:"service"` - Overrides interface{} - namespace string - deployment string - chart string +type ServiceGroupItem struct { + ServiceName string `yaml:"service"` + Overrides interface{} + namespace string + serviceGroup string + chart string } func generateYaml(path string, data interface{}) error { + log.Printf("Generating yaml file: %v", path) out, marshalErr := yaml.Marshal(data) if marshalErr != nil { return marshalErr @@ -38,12 +39,14 @@ func generateYaml(path string, data interface{}) error { return nil } -func generate(sd ServiceDeployment, outputDir, chartPath string) error { - dir := path.Join(outputDir, sd.ServiceName) +func generate(sgi ServiceGroupItem, outputDir, chartPath string) error { + dir := path.Join(outputDir, sgi.ServiceName) + log.Printf("Creating dir: %v", dir) if err := os.MkdirAll(dir, 0744); err != nil { return fmt.Errorf("could not create output dir: %w", err) } + log.Printf("Copying %v to %v", chartPath, dir) err := filepath.Walk(chartPath, func(source string, info os.FileInfo, err error) error { if err != nil { return err @@ -51,7 +54,9 @@ func generate(sd ServiceDeployment, outputDir, chartPath string) error { if chartPath == source { return nil } - if err := copy.Copy(source, path.Join(dir, info.Name())); err != nil { + + destination := path.Join(dir, info.Name()) + if err := copy.Copy(source, destination); err != nil { return err } if info.IsDir() { @@ -63,20 +68,24 @@ func generate(sd ServiceDeployment, outputDir, chartPath string) error { return fmt.Errorf("could not copy template files: %w", err) } - err = generateYaml(path.Join(dir, defaultOverridesFile), sd.Overrides) + err = generateYaml(path.Join(dir, defaultOverridesFile), sgi.Overrides) if err != nil { return fmt.Errorf("could not generate %v: %w", defaultOverridesFile, err) } return nil } -func helmInstall(serviceName, namespace, basePath string) error { - chartPath := path.Join(basePath, defaultDeploymentDir, serviceName) +func helmInstall(s Service, namespace, outputDir, tag string) error { + log.Printf("Running helm install for service: %v", s.Name) + chartPath := path.Join(outputDir, s.Name) cmdArgs := []string{ - "install", serviceName, chartPath, + "install", s.Name, chartPath, "-f", path.Join(chartPath, defaultOverridesFile), "-n", namespace, - "--set", "fullnameOverride=" + serviceName, + "--set", "nameOverride=" + s.Name, + "--set", "fullnameOverride=" + s.Name, + "--set", "image.repository=" + s.Build.Image, + "--set", "image.tag=" + tag, } cmd := exec.Command("helm", cmdArgs...) @@ -84,15 +93,16 @@ func helmInstall(serviceName, namespace, basePath string) error { output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return fmt.Errorf("could not install a helm chart for service %v output: \n%v", serviceName, output) + return fmt.Errorf("could not install a helm chart for service %v output: \n%v", s.Name, output) } log.Print(string(out)) return nil } -func helmUninstall(sd ServiceDeployment) error { +func helmUninstall(sgi ServiceGroupItem) error { + log.Printf("Running helm uninstall for service: %v", sgi.ServiceName) cmdArgs := []string{ - "uninstall", "-n", sd.namespace, sd.ServiceName, + "uninstall", "-n", sgi.namespace, sgi.ServiceName, } cmd := exec.Command("helm", cmdArgs...) @@ -100,47 +110,51 @@ func helmUninstall(sd ServiceDeployment) error { output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return fmt.Errorf("could not uninstall a helm chart for namespace: %v, deployment: %v, output: \n%v", sd.namespace, sd.deployment, output) + return fmt.Errorf("could not uninstall a helm chart for namespace: %v, service group: %v, output: \n%v", sgi.namespace, sgi.serviceGroup, output) } log.Print(string(out)) return nil } -func (c Configurd) Deploy(sds []ServiceDeployment, basePath string) error { - if err := os.RemoveAll(defaultDeploymentDir); err != nil { - return fmt.Errorf("could not clean deployment directory: %w", err) +func (c Configurd) Deploy(sgis []ServiceGroupItem, deploymentDir, tag string) error { + outputDir := path.Join(defaultOutputDir, deploymentDir) + + log.Printf("Removing dir: %v", outputDir) + if err := os.RemoveAll(outputDir); err != nil { + return fmt.Errorf("could not clean workdir directory: %w", err) } - if err := os.MkdirAll(defaultDeploymentDir, 0744); err != nil { - return fmt.Errorf("could not create a deployments directory: %w", err) + log.Printf("Creating dir: %v", outputDir) + if err := os.MkdirAll(outputDir, 0744); err != nil { + return fmt.Errorf("could not create a workdir directory: %w", err) } - for _, sd := range sds { - err := generate(sd, defaultDeploymentDir, path.Join(defaultChartDir, sd.chart)) + for _, sgi := range sgis { + err := generate(sgi, outputDir, path.Join(defaultChartDir, sgi.chart)) if err != nil { return err } - err = helmInstall(sd.ServiceName, sd.namespace, basePath) + + } + for _, sgi := range sgis { + s, err := c.Service(sgi.ServiceName) + if err != nil { + return err + } + err = helmInstall(s, sgi.namespace, outputDir, tag) + if err != nil { + return err + } + } + + return nil +} + +func (c Configurd) Undeploy(sgis []ServiceGroupItem) error { + for _, sgi := range sgis { + err := helmUninstall(sgi) if err != nil { return err } } return nil } - -func (c Configurd) Undeploy(sds []ServiceDeployment) error { - for _, sd := range sds { - cmdArgs := []string{ - "uninstall", "-n", sd.namespace, sd.ServiceName, - } - cmd := exec.Command("helm", cmdArgs...) - - out, err := cmd.CombinedOutput() - output := strings.Replace(string(out), "\n", "\n\t", -1) - - if err != nil { - return fmt.Errorf("could not uninstall a helm chart for namespace: %v, deployment: %v, output: \n%v", sd.namespace, sd.deployment, output) - } - log.Print(string(out)) - } - return nil -} From 674b5ae4f2c9f6266bee9211afeeebd1abc3cc0b Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 10 Mar 2020 14:15:13 +0100 Subject: [PATCH 12/18] Add log dependency and refactor minor error handling --- cmd/actions/build.go | 17 +++--- cmd/actions/deploy.go | 46 -------------- cmd/actions/install.go | 53 ++++++++++++++++ cmd/actions/root.go | 23 ++++--- cmd/actions/undeploy.go | 39 ------------ cmd/actions/uninstall.go | 43 +++++++++++++ configurd.go | 32 ++++++---- docker.go | 8 +-- example/configurd/services/hello-service.yml | 2 +- example/configurd/services/hi-service.yml | 2 +- go.mod | 7 ++- go.sum | 64 +++++--------------- helm.go | 61 ++++++++++--------- 13 files changed, 191 insertions(+), 206 deletions(-) delete mode 100644 cmd/actions/deploy.go create mode 100644 cmd/actions/install.go delete mode 100644 cmd/actions/undeploy.go create mode 100644 cmd/actions/uninstall.go diff --git a/cmd/actions/build.go b/cmd/actions/build.go index 039598d..7e59613 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -1,7 +1,7 @@ package actions import ( - "log" + "fmt" "github.com/spf13/cobra" ) @@ -11,7 +11,10 @@ var cmdBuild = &cobra.Command{ Short: "Build a service", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - Build(args[0], cmd.Flag("tag").Value.String()) + _, err := Build(args[0], FlagTag, FlagDir) + if err != nil { + log.Fatal(err) + } }, } @@ -19,14 +22,14 @@ func init() { rootCmd.AddCommand(cmdBuild) } -func Build(service, tag string) { +func Build(service, tag, dir string) (string, error) { svc, err := cnf.Service(service) if err != nil { - log.Fatalf("service not found: %v", err) + return "", fmt.Errorf("service not found: %v", err) } - output, err := svc.RunBuild(tag) + output, err := svc.RunBuild(log, dir, tag) if err != nil { - log.Fatalf("could not build: %v output:\n%v", output, err) + return "", fmt.Errorf("could not build service: %v output:\n%v", svc.Name, output) } - log.Print(output) + return output, nil } diff --git a/cmd/actions/deploy.go b/cmd/actions/deploy.go deleted file mode 100644 index 0baf0d7..0000000 --- a/cmd/actions/deploy.go +++ /dev/null @@ -1,46 +0,0 @@ -package actions - -import ( - "log" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(deployCmd) - deployCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build service group before publishing") - deployCmd.Flags().StringVarP(&flagOutputDir, "output", "o", "default", "Specifies output directory") -} - -var ( - flagBuild bool - flagOutputDir string -) - -var ( - deployCmd = &cobra.Command{ - Use: "deploy [NAMESPACE] [SERVICE GROUP] -t {TAG}", - Short: "Deploys the specified service group with given tag version", - Long: `Deploys the specified service group with given tag version`, - Args: cobra.MinimumNArgs(2), - Run: func(cmd *cobra.Command, args []string) { - Deploy(args[0], args[1], FlagTag) - }, - } -) - -func Deploy(namespace, serviceGroup, tag string) { - sgis := cnf.GetServiceGroupItems(namespace, serviceGroup) - if len(sgis) == 0 { - log.Fatalf("could not find any service for namespace: %v and service group: %v", namespace, serviceGroup) - } - if flagBuild { - for _, sgi := range sgis { - Build(sgi.ServiceName, tag) - } - } - err := cnf.Deploy(sgis, flagOutputDir, tag) - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/actions/install.go b/cmd/actions/install.go new file mode 100644 index 0000000..bfe53f3 --- /dev/null +++ b/cmd/actions/install.go @@ -0,0 +1,53 @@ +package actions + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(installCmd) + installCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build service group before publishing") + installCmd.Flags().StringVarP(&flagOutputDir, "output", "o", "default", "Specifies output directory") +} + +var ( + flagBuild bool + flagOutputDir string +) + +var ( + installCmd = &cobra.Command{ + Use: "install [NAMESPACE] [SERVICE GROUP] -t {TAG}", + Short: "installs the specified service group with given tag version", + Long: "installs the specified service group with given tag version", + Args: cobra.MinimumNArgs(2), + Run: func(cmd *cobra.Command, args []string) { + _, err := install(args[0], args[1], FlagTag, FlagDir, flagBuild) + if err != nil { + log.Fatal(err) + } + }, + } +) + +func install(namespace, serviceGroup, tag, dir string, shouldBuild bool) (string, error) { + sgis := cnf.GetServiceGroupItems(namespace, serviceGroup) + if len(sgis) == 0 { + return "", fmt.Errorf("could not find any service for namespace: %v and service group: %v", namespace, serviceGroup) + } + if shouldBuild { + for _, sgi := range sgis { + _, err := Build(sgi.ServiceName, tag, dir) + if err != nil { + return "", err + } + } + } + output, err := cnf.Install(log, sgis, flagOutputDir, tag) + if err != nil { + return "", fmt.Errorf("could not install service group: %v output:\n%v", serviceGroup, output) + } + return output, nil +} diff --git a/cmd/actions/root.go b/cmd/actions/root.go index 030633b..1ad165f 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -1,17 +1,16 @@ package actions import ( - "fmt" - "log" "os" "github.com/foomo/configurd" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) var ( + log = logrus.New() cnf configurd.Configurd - cwdir string rootCmd = &cobra.Command{ Use: "cobra", Short: "A generator for Cobra based Applications", @@ -21,26 +20,26 @@ to quickly create a Cobra application.`, } FlagTag string + FlagDir string ) func init() { - rootCmd.PersistentFlags().StringVarP(&FlagTag, "tag", "t", "latest", "Specifies the image tag") -} - -func Execute() { - var err error = nil - cwdir, err = os.Getwd() + baseDir, err := os.Getwd() if err != nil { log.Fatal(err) } + rootCmd.PersistentFlags().StringVarP(&FlagTag, "tag", "t", "latest", "Specifies the image tag") + rootCmd.PersistentFlags().StringVarP(&FlagDir, "dir", "d", baseDir, "Specifies working directory") +} - cnf, err = configurd.New(cwdir) +func Execute() { + var err error + cnf, err = configurd.New(log, FlagDir) if err != nil { log.Fatal(err) } if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) + log.Fatal(err) } } diff --git a/cmd/actions/undeploy.go b/cmd/actions/undeploy.go deleted file mode 100644 index 070b2ca..0000000 --- a/cmd/actions/undeploy.go +++ /dev/null @@ -1,39 +0,0 @@ -package actions - -import ( - "log" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(undeployCmd) - undeployCmd.Flags().StringVarP(&FlagSG, "sg", "g", "", "Specifies the service group name") -} - -var ( - FlagSG string -) - -var ( - undeployCmd = &cobra.Command{ - Use: "undeploy [NAMESPACE] -g {SERVICE GROUP}", - Short: "Uneploys the specified service group with given tag version", - Long: `Uneploys the specified service group with given tag version`, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - Undeploy(args[0], FlagSG) - }, - } -) - -func Undeploy(namespace, deployment string) { - sgis := cnf.GetServiceGroupItems(namespace, deployment) - if len(sgis) == 0 { - log.Fatalf("could not find any service for namespace: %v and service group: %v", namespace, deployment) - } - err := cnf.Undeploy(sgis) - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/actions/uninstall.go b/cmd/actions/uninstall.go new file mode 100644 index 0000000..bd28a9e --- /dev/null +++ b/cmd/actions/uninstall.go @@ -0,0 +1,43 @@ +package actions + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(uninstallCmd) + uninstallCmd.Flags().StringVarP(&FlagServiceGroup, "service-group", "g", "", "Specifies the service group name") +} + +var ( + FlagServiceGroup string +) + +var ( + uninstallCmd = &cobra.Command{ + Use: "uninstall [NAMESPACE] -g {SERVICE GROUP}", + Short: "uninstalls the specified service group with given tag version", + Long: "uninstalls the specified service group with given tag version", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + _, err := uninstall(args[0], FlagServiceGroup) + if err != nil { + log.Fatal(err) + } + }, + } +) + +func uninstall(namespace, serviceGroup string) (string, error) { + sgis := cnf.GetServiceGroupItems(namespace, serviceGroup) + if len(sgis) == 0 { + return "", fmt.Errorf("could not find any service for namespace: %v and service group: %v", namespace, serviceGroup) + } + output, err := cnf.Uninstall(log, sgis) + if err != nil { + return "", fmt.Errorf("could not uninstall service group: %v output:\n%v", serviceGroup, output) + } + return output, nil +} diff --git a/configurd.go b/configurd.go index 4296f4b..14794c5 100644 --- a/configurd.go +++ b/configurd.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "io" - "log" "os" "path" "path/filepath" @@ -42,7 +41,14 @@ type Configurd struct { Namespaces []Namespace } -func New(dir string) (Configurd, error) { +type serviceLoader func(string) (Service, error) + +type Logger interface { + Printf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +func New(log Logger, dir string) (Configurd, error) { log.Printf("Parsing configuration files") c := Configurd{} @@ -73,7 +79,7 @@ func New(dir string) (Configurd, error) { return Configurd{}, err } - c.Namespaces, err = loadNamespaces(c, dir) + c.Namespaces, err = loadNamespaces(log, c.Service, dir) if err != nil { return Configurd{}, err @@ -82,7 +88,7 @@ func New(dir string) (Configurd, error) { return c, nil } -func loadNamespaces(c Configurd, basePath string) ([]Namespace, error) { +func loadNamespaces(log Logger, sl serviceLoader, basePath string) ([]Namespace, error) { var nss []Namespace namespaceDir := path.Join(basePath, defaultNamespaceDir) err := filepath.Walk(namespaceDir, func(path string, info os.FileInfo, err error) error { @@ -91,7 +97,7 @@ func loadNamespaces(c Configurd, basePath string) ([]Namespace, error) { } if info.IsDir() && path != namespaceDir { log.Printf("Loading namespace: %v, from: %v", info.Name(), path) - sgs, err := loadServiceGroups(c, info.Name()) + sgs, err := loadServiceGroups(log, sl, info.Name()) if err != nil { return err } @@ -106,7 +112,7 @@ func loadNamespaces(c Configurd, basePath string) ([]Namespace, error) { return nss, err } -func loadServiceGroups(c Configurd, namespace string) ([]ServiceGroup, error) { +func loadServiceGroups(log Logger, sl serviceLoader, namespace string) ([]ServiceGroup, error) { var sgs []ServiceGroup sgDir := path.Join(defaultNamespaceDir, namespace) @@ -117,7 +123,7 @@ func loadServiceGroups(c Configurd, namespace string) ([]ServiceGroup, error) { if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] log.Printf("Loading service group: %v, from: %v", name, path) - sgis, err := loadServiceGroupItems(c, namespace, name) + sgis, err := loadServiceGroupItems(log, sl, namespace, name) if err != nil { return err } @@ -132,26 +138,26 @@ func loadServiceGroups(c Configurd, namespace string) ([]ServiceGroup, error) { return sgs, err } -func loadServiceGroupItems(c Configurd, namespace, serviceGroup string) ([]ServiceGroupItem, error) { +func loadServiceGroupItems(log Logger, sl serviceLoader, namespace, serviceGroup string) ([]ServiceGroupItem, error) { path := path.Join(defaultNamespaceDir, namespace, serviceGroup+defaultConfigFileExt) file, err := os.Open(path) - defer file.Close() if err != nil { - return []ServiceGroupItem{}, err + return nil, err } + defer file.Close() var wrapper struct { Items []ServiceGroupItem `yaml:"serviceGroup"` } if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { - return []ServiceGroupItem{}, fmt.Errorf("could not decode service group items: %w", err) + return nil, fmt.Errorf("could not decode service group items: %w", err) } for i, sgi := range wrapper.Items { log.Printf("Loading service group item: %v", sgi.ServiceName) - svc, err := c.Service(sgi.ServiceName) + svc, err := sl(sgi.ServiceName) if err != nil { - log.Fatal(err) + return nil, err } wrapper.Items[i].chart = svc.Chart wrapper.Items[i].namespace = namespace diff --git a/docker.go b/docker.go index 256db1b..c9a3a13 100644 --- a/docker.go +++ b/docker.go @@ -1,7 +1,7 @@ package configurd import ( - "log" + "fmt" "os/exec" "strings" ) @@ -17,12 +17,12 @@ type Service struct { Chart string `yaml:"chart"` } -func (s Service) RunBuild(tag string) (string, error) { - args := append(strings.Split(s.Build.Command, " "), "-t", s.Build.Image+":"+tag) +func (s Service) RunBuild(log Logger, dir, tag string) (string, error) { + args := append(strings.Split(s.Build.Command, " "), "-t", fmt.Sprintf("%v:%v", s.Build.Image, tag)) log.Printf("Running command: %v", strings.Join(args, " ")) cmd := exec.Command(args[0], args[1:]...) - cmd.Dir = defaultServiceDir + cmd.Dir = dir out, err := cmd.CombinedOutput() output := strings.Replace(string(out), "\n", "\n\t", -1) diff --git a/example/configurd/services/hello-service.yml b/example/configurd/services/hello-service.yml index 2284653..6a3c228 100644 --- a/example/configurd/services/hello-service.yml +++ b/example/configurd/services/hello-service.yml @@ -1,5 +1,5 @@ service: build: - command: "docker build ../../application/" + command: "docker build application/" image: foomo/configurd-hello chart: example diff --git a/example/configurd/services/hi-service.yml b/example/configurd/services/hi-service.yml index 01e5372..0f7531f 100644 --- a/example/configurd/services/hi-service.yml +++ b/example/configurd/services/hi-service.yml @@ -1,5 +1,5 @@ service: build: - command: "docker build ../../application/" + command: "docker build application/" image: foomo/configurd-hi chart: example diff --git a/go.mod b/go.mod index 7b2e248..27dae9d 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/foomo/configurd go 1.14 require ( - github.com/go-delve/delve v1.4.0 // indirect - github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6 // indirect github.com/otiai10/copy v1.0.2 + github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.6 - github.com/stretchr/testify v1.3.0 + github.com/stretchr/testify v1.4.0 + golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index 6fd2749..3a76ed1 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -13,34 +15,19 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= -github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= -github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/foomo/gorpc v0.0.0-20170320121619-aafa90603875/go.mod h1:0uzKetayf2X/UDMGiv0xH64DU22OcIZxWlAColGJzOY= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-delve/delve v1.4.0 h1:O+1dw1XBZXqhC6fIPQwGxLlbd2wDRau7NxNhVpw02ag= -github.com/go-delve/delve v1.4.0/go.mod h1:gQM0ReOJLNAvPuKAXfjHngtE93C2yc/ekTbo7YbAHSo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6 h1:iRn4qXDcqlb2sFqTdyTdYKjaPoLka7tvcyF+FZA9/qw= -github.com/godoctor/godoctor v0.0.0-20181123222458-69df17f3a6f6/go.mod h1:+tyhT8jBF8E0XvdlSXOSL7Iko7DlNiongHq3q+wcsPs= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -56,13 +43,13 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -72,22 +59,13 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/moby v1.13.1 h1:mC5WwQwCXt/dYxZ1cIrRsnJAWw7VdtcTZUIGr4tXzOM= -github.com/moby/moby v1.13.1/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/otiai10/copy v1.0.2 h1:DDNipYy6RkIkjMwy+AWzgKiNTyj2RUI9yEMeETEpVyc= github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 h1:+OLn68pqasWca0z5ryit9KGfp3sUsW4Lqg32iRMJyzs= @@ -96,10 +74,8 @@ github.com/otiai10/mint v1.3.0 h1:Ady6MKVezQwHBkGzLFbrsywyp09Ah7rkmfjV3Bcr5uc= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -107,31 +83,30 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= @@ -140,72 +115,61 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI= -golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f h1:3MlESg/jvTr87F4ttA/q4B+uhe/q6qleC9/DP+IwQmY= -golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/helm.go b/helm.go index 8eecde5..aa71173 100644 --- a/helm.go +++ b/helm.go @@ -2,7 +2,6 @@ package configurd import ( "fmt" - "log" "os" "os/exec" "path" @@ -21,7 +20,7 @@ type ServiceGroupItem struct { chart string } -func generateYaml(path string, data interface{}) error { +func generateYaml(log Logger, path string, data interface{}) error { log.Printf("Generating yaml file: %v", path) out, marshalErr := yaml.Marshal(data) if marshalErr != nil { @@ -39,7 +38,7 @@ func generateYaml(path string, data interface{}) error { return nil } -func generate(sgi ServiceGroupItem, outputDir, chartPath string) error { +func generate(log Logger, sgi ServiceGroupItem, outputDir, chartPath string) error { dir := path.Join(outputDir, sgi.ServiceName) log.Printf("Creating dir: %v", dir) if err := os.MkdirAll(dir, 0744); err != nil { @@ -68,24 +67,24 @@ func generate(sgi ServiceGroupItem, outputDir, chartPath string) error { return fmt.Errorf("could not copy template files: %w", err) } - err = generateYaml(path.Join(dir, defaultOverridesFile), sgi.Overrides) + err = generateYaml(log, path.Join(dir, defaultOverridesFile), sgi.Overrides) if err != nil { return fmt.Errorf("could not generate %v: %w", defaultOverridesFile, err) } return nil } -func helmInstall(s Service, namespace, outputDir, tag string) error { +func helmInstall(log Logger, s Service, namespace, outputDir, tag string) (string, error) { log.Printf("Running helm install for service: %v", s.Name) chartPath := path.Join(outputDir, s.Name) cmdArgs := []string{ "install", s.Name, chartPath, "-f", path.Join(chartPath, defaultOverridesFile), "-n", namespace, - "--set", "nameOverride=" + s.Name, - "--set", "fullnameOverride=" + s.Name, - "--set", "image.repository=" + s.Build.Image, - "--set", "image.tag=" + tag, + "--set", fmt.Sprintf("nameOverride=%v", s.Name), + "--set", fmt.Sprintf("fullnameOverride=%v", s.Name), + "--set", fmt.Sprintf("image.repository=%v", s.Build.Image), + "--set", fmt.Sprintf("image.tag=%v", tag), } cmd := exec.Command("helm", cmdArgs...) @@ -93,13 +92,12 @@ func helmInstall(s Service, namespace, outputDir, tag string) error { output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return fmt.Errorf("could not install a helm chart for service %v output: \n%v", s.Name, output) + return "", fmt.Errorf("could not install a helm chart for service %v output: \n%v", s.Name, output) } - log.Print(string(out)) - return nil + return output, nil } -func helmUninstall(sgi ServiceGroupItem) error { +func helmUninstall(log Logger, sgi ServiceGroupItem) (string, error) { log.Printf("Running helm uninstall for service: %v", sgi.ServiceName) cmdArgs := []string{ "uninstall", "-n", sgi.namespace, sgi.ServiceName, @@ -110,51 +108,54 @@ func helmUninstall(sgi ServiceGroupItem) error { output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return fmt.Errorf("could not uninstall a helm chart for namespace: %v, service group: %v, output: \n%v", sgi.namespace, sgi.serviceGroup, output) + return "", fmt.Errorf("could not uninstall a helm chart for namespace: %v, service group: %v, output: \n%v", sgi.namespace, sgi.serviceGroup, output) } - log.Print(string(out)) - return nil + return output, nil } -func (c Configurd) Deploy(sgis []ServiceGroupItem, deploymentDir, tag string) error { +func (c Configurd) Install(log Logger, sgis []ServiceGroupItem, deploymentDir, tag string) (string, error) { outputDir := path.Join(defaultOutputDir, deploymentDir) log.Printf("Removing dir: %v", outputDir) if err := os.RemoveAll(outputDir); err != nil { - return fmt.Errorf("could not clean workdir directory: %w", err) + return "", fmt.Errorf("could not clean workdir directory: %w", err) } log.Printf("Creating dir: %v", outputDir) if err := os.MkdirAll(outputDir, 0744); err != nil { - return fmt.Errorf("could not create a workdir directory: %w", err) + return "", fmt.Errorf("could not create a workdir directory: %w", err) } for _, sgi := range sgis { - err := generate(sgi, outputDir, path.Join(defaultChartDir, sgi.chart)) + err := generate(log, sgi, outputDir, path.Join(defaultChartDir, sgi.chart)) if err != nil { - return err + return "", err } - } + + var output []string for _, sgi := range sgis { s, err := c.Service(sgi.ServiceName) if err != nil { - return err + return "", err } - err = helmInstall(s, sgi.namespace, outputDir, tag) + out, err := helmInstall(log, s, sgi.namespace, outputDir, tag) if err != nil { - return err + return "", err } + output = append(output, out) } - return nil + return strings.Join(output, "\n"), nil } -func (c Configurd) Undeploy(sgis []ServiceGroupItem) error { +func (c Configurd) Uninstall(log Logger, sgis []ServiceGroupItem) (string, error) { + var output []string for _, sgi := range sgis { - err := helmUninstall(sgi) + out, err := helmUninstall(log, sgi) if err != nil { - return err + return "", err } + output = append(output, out) } - return nil + return strings.Join(output, "\n"), nil } From eb73ab1fb65eb1ab8607cc96e9d22489f134dacb Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Wed, 11 Mar 2020 14:46:47 +0100 Subject: [PATCH 13/18] Refactor logging and some flags --- .gitignore | 2 + cmd/actions/build.go | 10 +-- cmd/actions/install.go | 36 +++++--- cmd/actions/root.go | 8 +- cmd/actions/uninstall.go | 27 +++--- configurd.go | 88 +++++++++++-------- docker.go | 5 +- example/Makefile | 14 ++- .../local/{hello-sg.yml => hello-group.yml} | 2 +- helm.go | 86 ++++++++++-------- 10 files changed, 163 insertions(+), 115 deletions(-) rename example/configurd/namespaces/local/{hello-sg.yml => hello-group.yml} (92%) diff --git a/.gitignore b/.gitignore index d899431..3689634 100644 --- a/.gitignore +++ b/.gitignore @@ -120,4 +120,6 @@ modules.xml # Ignore all local history of files .history +.todo + # End of https://www.gitignore.io/api/go,jetbrains+all,visualstudiocode \ No newline at end of file diff --git a/cmd/actions/build.go b/cmd/actions/build.go index 7e59613..eb850e3 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -7,11 +7,11 @@ import ( ) var cmdBuild = &cobra.Command{ - Use: "build [configuration file to use for service] -t TAG", - Short: "Build a service", + Use: "build [SERVICE] -t {TAG}", + Short: "Build a service with a given tag", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - _, err := Build(args[0], FlagTag, FlagDir) + _, err := Build(args[0], FlagTag, FlagDir, FlagVerbose) if err != nil { log.Fatal(err) } @@ -22,12 +22,12 @@ func init() { rootCmd.AddCommand(cmdBuild) } -func Build(service, tag, dir string) (string, error) { +func Build(service, tag, dir string, flagVerbose bool) (string, error) { svc, err := cnf.Service(service) if err != nil { return "", fmt.Errorf("service not found: %v", err) } - output, err := svc.RunBuild(log, dir, tag) + output, err := svc.RunBuild(log, dir, tag, flagVerbose) if err != nil { return "", fmt.Errorf("could not build service: %v output:\n%v", svc.Name, output) } diff --git a/cmd/actions/install.go b/cmd/actions/install.go index bfe53f3..1001916 100644 --- a/cmd/actions/install.go +++ b/cmd/actions/install.go @@ -10,6 +10,8 @@ func init() { rootCmd.AddCommand(installCmd) installCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build service group before publishing") installCmd.Flags().StringVarP(&flagOutputDir, "output", "o", "default", "Specifies output directory") + installCmd.Flags().StringVarP(&FlagNamespace, "namespace", "n", "default", "Specifies the namespace") + // installCmd.Flags().StringVarP(&FlagGroup, "group", "g", "", "Specifies the group") } var ( @@ -19,12 +21,12 @@ var ( var ( installCmd = &cobra.Command{ - Use: "install [NAMESPACE] [SERVICE GROUP] -t {TAG}", - Short: "installs the specified service group with given tag version", - Long: "installs the specified service group with given tag version", - Args: cobra.MinimumNArgs(2), + Use: "install [GROUP] -n {NAMESPACE} -t {TAG}", + Short: "installs the specified group with given namespace and tag version", + Long: "installs the specified group with given namespace and tag version", + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - _, err := install(args[0], args[1], FlagTag, FlagDir, flagBuild) + _, err := install(args[0], FlagNamespace, FlagTag, FlagDir, flagOutputDir, flagBuild, FlagVerbose) if err != nil { log.Fatal(err) } @@ -32,22 +34,28 @@ var ( } ) -func install(namespace, serviceGroup, tag, dir string, shouldBuild bool) (string, error) { - sgis := cnf.GetServiceGroupItems(namespace, serviceGroup) - if len(sgis) == 0 { - return "", fmt.Errorf("could not find any service for namespace: %v and service group: %v", namespace, serviceGroup) +func install(group, namespace, tag, workDir, outputDir string, build, verbose bool) (string, error) { + sis := cnf.GetServiceItems(namespace, group) + if len(sis) == 0 { + return "", fmt.Errorf("could not find any service for namespace: %v and group: %v", namespace, group) } - if shouldBuild { - for _, sgi := range sgis { - _, err := Build(sgi.ServiceName, tag, dir) + if build { + log.Printf("Building services") + for _, si := range sis { + _, err := Build(si.ServiceName, tag, workDir, verbose) if err != nil { return "", err } } } - output, err := cnf.Install(log, sgis, flagOutputDir, tag) + output, err := cnf.Install(log, sis, workDir, outputDir, tag, verbose) if err != nil { - return "", fmt.Errorf("could not install service group: %v output:\n%v", serviceGroup, output) + // return "", fmt.Errorf("could not install group: %v output:\n%v \nerror: \n%v", group, output, err) + return "", errorf(output, err, "could not install group: %v", group) } return output, nil } + +func errorf(output string, err error, format string, args ...interface{}) error { + return fmt.Errorf("%v, error: %v", fmt.Sprintf(format, args...), err) +} diff --git a/cmd/actions/root.go b/cmd/actions/root.go index 1ad165f..bebd001 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -19,8 +19,11 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, } - FlagTag string - FlagDir string + FlagTag string + FlagDir string + FlagVerbose bool + FlagNamespace string + // FlagGroup string ) func init() { @@ -30,6 +33,7 @@ func init() { } rootCmd.PersistentFlags().StringVarP(&FlagTag, "tag", "t", "latest", "Specifies the image tag") rootCmd.PersistentFlags().StringVarP(&FlagDir, "dir", "d", baseDir, "Specifies working directory") + rootCmd.PersistentFlags().BoolVarP(&FlagVerbose, "verbose", "v", false, "Specifies should command output be displayed") } func Execute() { diff --git a/cmd/actions/uninstall.go b/cmd/actions/uninstall.go index bd28a9e..82ef94b 100644 --- a/cmd/actions/uninstall.go +++ b/cmd/actions/uninstall.go @@ -8,21 +8,18 @@ import ( func init() { rootCmd.AddCommand(uninstallCmd) - uninstallCmd.Flags().StringVarP(&FlagServiceGroup, "service-group", "g", "", "Specifies the service group name") + uninstallCmd.Flags().StringVarP(&FlagNamespace, "namespace", "n", "default", "Specifies the namespace") + // uninstallCmd.Flags().StringVarP(&FlagGroup, "group", "g", "", "Specifies the group") } -var ( - FlagServiceGroup string -) - var ( uninstallCmd = &cobra.Command{ - Use: "uninstall [NAMESPACE] -g {SERVICE GROUP}", - Short: "uninstalls the specified service group with given tag version", - Long: "uninstalls the specified service group with given tag version", + Use: "uninstall [GROUP] -n {NAMESPACE} -t {TAG}", + Short: "uninstalls the specified group with given namespace and tag version", + Long: "uninstalls the specified group with given namespace and tag version", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - _, err := uninstall(args[0], FlagServiceGroup) + _, err := uninstall(args[0], FlagNamespace, FlagVerbose) if err != nil { log.Fatal(err) } @@ -30,14 +27,14 @@ var ( } ) -func uninstall(namespace, serviceGroup string) (string, error) { - sgis := cnf.GetServiceGroupItems(namespace, serviceGroup) - if len(sgis) == 0 { - return "", fmt.Errorf("could not find any service for namespace: %v and service group: %v", namespace, serviceGroup) +func uninstall(group, namespace string, flagVerbose bool) (string, error) { + sis := cnf.GetServiceItems(namespace, group) + if len(sis) == 0 { + return "", fmt.Errorf("could not find any service for namespace: %v and service group: %v", namespace, group) } - output, err := cnf.Uninstall(log, sgis) + output, err := cnf.Uninstall(log, sis, flagVerbose) if err != nil { - return "", fmt.Errorf("could not uninstall service group: %v output:\n%v", serviceGroup, output) + return "", fmt.Errorf("could not uninstall service group: %v output:\n%v", group, output) } return output, nil } diff --git a/configurd.go b/configurd.go index 14794c5..0416cc5 100644 --- a/configurd.go +++ b/configurd.go @@ -25,14 +25,15 @@ var ( ErrServiceNotFound = errors.New("service not found") ) -type ServiceGroup struct { - name string - items []ServiceGroupItem +type Group struct { + name string + Services []ServiceItem + Jobs []JobItem } type Namespace struct { - name string - serviceGroups []ServiceGroup + name string + groups []Group } type Configurd struct { @@ -48,11 +49,16 @@ type Logger interface { Fatalf(format string, args ...interface{}) } +func relativePath(absoltePath, basePath string) string { + return strings.Replace(absoltePath, basePath+"/", "", -1) +} + func New(log Logger, dir string) (Configurd, error) { log.Printf("Parsing configuration files") c := Configurd{} serviceDir := path.Join(dir, defaultServiceDir) + log.Printf("Entering dir: %q", serviceDir) err := filepath.Walk(serviceDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -65,7 +71,7 @@ func New(log Logger, dir string) (Configurd, error) { defer file.Close() var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] - log.Printf("Loading service: %v, from: %v", name, path) + log.Printf("Loading service: %v, from file: %q", name, relativePath(path, serviceDir)) svc, err := loadService(file, name) if err != nil { return err @@ -91,19 +97,20 @@ func New(log Logger, dir string) (Configurd, error) { func loadNamespaces(log Logger, sl serviceLoader, basePath string) ([]Namespace, error) { var nss []Namespace namespaceDir := path.Join(basePath, defaultNamespaceDir) + log.Printf("Entering dir: %q", namespaceDir) err := filepath.Walk(namespaceDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() && path != namespaceDir { - log.Printf("Loading namespace: %v, from: %v", info.Name(), path) - sgs, err := loadServiceGroups(log, sl, info.Name()) + log.Printf("Loading namespace: %v, from dir: %q", info.Name(), relativePath(path, namespaceDir)) + sgs, err := loadGroups(log, sl, basePath, info.Name()) if err != nil { return err } ns := Namespace{ - name: info.Name(), - serviceGroups: sgs, + name: info.Name(), + groups: sgs, } nss = append(nss, ns) } @@ -112,24 +119,25 @@ func loadNamespaces(log Logger, sl serviceLoader, basePath string) ([]Namespace, return nss, err } -func loadServiceGroups(log Logger, sl serviceLoader, namespace string) ([]ServiceGroup, error) { - var sgs []ServiceGroup +func loadGroups(log Logger, sl serviceLoader, basePath, namespace string) ([]Group, error) { + var sgs []Group - sgDir := path.Join(defaultNamespaceDir, namespace) - err := filepath.Walk(sgDir, func(path string, info os.FileInfo, err error) error { + groupDir := path.Join(basePath, defaultNamespaceDir, namespace) + log.Printf("Entering dir: %q", groupDir) + err := filepath.Walk(groupDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] - log.Printf("Loading service group: %v, from: %v", name, path) - sgis, err := loadServiceGroupItems(log, sl, namespace, name) + log.Printf("Loading service group: %v, from file: %q", name, relativePath(path, groupDir)) + sis, err := loadServiceItems(log, sl, namespace, name) if err != nil { return err } - sg := ServiceGroup{ - name: name, - items: sgis, + sg := Group{ + name: name, + Services: sis, } sgs = append(sgs, sg) } @@ -138,8 +146,8 @@ func loadServiceGroups(log Logger, sl serviceLoader, namespace string) ([]Servic return sgs, err } -func loadServiceGroupItems(log Logger, sl serviceLoader, namespace, serviceGroup string) ([]ServiceGroupItem, error) { - path := path.Join(defaultNamespaceDir, namespace, serviceGroup+defaultConfigFileExt) +func loadServiceItems(log Logger, sl serviceLoader, namespace, group string) ([]ServiceItem, error) { + path := path.Join(defaultNamespaceDir, namespace, group+defaultConfigFileExt) file, err := os.Open(path) if err != nil { return nil, err @@ -147,21 +155,21 @@ func loadServiceGroupItems(log Logger, sl serviceLoader, namespace, serviceGroup defer file.Close() var wrapper struct { - Items []ServiceGroupItem `yaml:"serviceGroup"` + Items []ServiceItem `yaml:"group"` } if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { return nil, fmt.Errorf("could not decode service group items: %w", err) } - for i, sgi := range wrapper.Items { - log.Printf("Loading service group item: %v", sgi.ServiceName) - svc, err := sl(sgi.ServiceName) + for i, si := range wrapper.Items { + log.Printf("Loading service group item: %v", si.ServiceName) + svc, err := sl(si.ServiceName) if err != nil { return nil, err } wrapper.Items[i].chart = svc.Chart wrapper.Items[i].namespace = namespace - wrapper.Items[i].serviceGroup = serviceGroup + wrapper.Items[i].group = group } return wrapper.Items, nil } @@ -175,24 +183,24 @@ func (c Configurd) Service(name string) (Service, error) { return Service{}, ErrServiceNotFound } -func (c Configurd) GetServiceGroupItems(namespace, serviceGroup string) []ServiceGroupItem { - var sgis []ServiceGroupItem - for _, sgi := range c.serviceGroupItems() { - if sgi.namespace == namespace && (sgi.serviceGroup == serviceGroup || serviceGroup == "") { - sgis = append(sgis, sgi) +func (c Configurd) GetServiceItems(namespace, group string) []ServiceItem { + var sis []ServiceItem + for _, si := range c.serviceItems() { + if si.namespace == namespace && (si.group == group || group == "") { + sis = append(sis, si) } } - return sgis + return sis } -func (c Configurd) serviceGroupItems() []ServiceGroupItem { - var sgis []ServiceGroupItem +func (c Configurd) serviceItems() []ServiceItem { + var sis []ServiceItem for _, ns := range c.Namespaces { - for _, sg := range ns.serviceGroups { - sgis = append(sgis, sg.items...) + for _, sg := range ns.groups { + sis = append(sis, sg.Services...) } } - return sgis + return sis } func loadService(reader io.Reader, name string) (Service, error) { @@ -205,3 +213,9 @@ func loadService(reader io.Reader, name string) (Service, error) { wrapper.Service.Name = name return wrapper.Service, nil } + +func logOutput(log Logger, verbose bool, format string, args ...interface{}) { + if verbose { + log.Printf(format, args...) + } +} diff --git a/docker.go b/docker.go index c9a3a13..33d1ae1 100644 --- a/docker.go +++ b/docker.go @@ -17,9 +17,9 @@ type Service struct { Chart string `yaml:"chart"` } -func (s Service) RunBuild(log Logger, dir, tag string) (string, error) { +func (s Service) RunBuild(log Logger, dir, tag string, verbose bool) (string, error) { args := append(strings.Split(s.Build.Command, " "), "-t", fmt.Sprintf("%v:%v", s.Build.Image, tag)) - log.Printf("Running command: %v", strings.Join(args, " ")) + log.Printf("Building service: %v", s.Name) cmd := exec.Command(args[0], args[1:]...) cmd.Dir = dir @@ -29,5 +29,6 @@ func (s Service) RunBuild(log Logger, dir, tag string) (string, error) { if err != nil { return output, err } + logOutput(log, verbose, output) return output, nil } diff --git a/example/Makefile b/example/Makefile index 6008116..9e6ff59 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,8 +1,14 @@ build: go run ../cmd/main.go build hello-service -t master -deploy: - go run ../cmd/main.go deploy local hello-deployment -t master +install: + go run ../cmd/main.go install local hello-group -t master --build -undeploy: - go run ../cmd/main.go undeploy local -d hello-deployment +uninstall: + go run ../cmd/main.go uninstall local -g hello-group + +install-olaf: + go run ../cmd/main.go install olaf-group -n local -t latest --build -v + +uninstall-olaf: + go run ../cmd/main.go uninstall olaf-group -n local diff --git a/example/configurd/namespaces/local/hello-sg.yml b/example/configurd/namespaces/local/hello-group.yml similarity index 92% rename from example/configurd/namespaces/local/hello-sg.yml rename to example/configurd/namespaces/local/hello-group.yml index b135e73..c30bf41 100644 --- a/example/configurd/namespaces/local/hello-sg.yml +++ b/example/configurd/namespaces/local/hello-group.yml @@ -1,4 +1,4 @@ -serviceGroup: +group: - service: "hello-service" overrides: args: diff --git a/helm.go b/helm.go index aa71173..57f7fc9 100644 --- a/helm.go +++ b/helm.go @@ -12,16 +12,23 @@ import ( "gopkg.in/yaml.v2" ) -type ServiceGroupItem struct { - ServiceName string `yaml:"service"` - Overrides interface{} - namespace string - serviceGroup string - chart string +type ServiceItem struct { + ServiceName string `yaml:"service"` + Overrides interface{} + namespace string + group string + chart string +} + +type JobItem struct { + ServiceName string `yaml:"service"` + Overrides interface{} + namespace string + group string + chart string } func generateYaml(log Logger, path string, data interface{}) error { - log.Printf("Generating yaml file: %v", path) out, marshalErr := yaml.Marshal(data) if marshalErr != nil { return marshalErr @@ -38,14 +45,17 @@ func generateYaml(log Logger, path string, data interface{}) error { return nil } -func generate(log Logger, sgi ServiceGroupItem, outputDir, chartPath string) error { - dir := path.Join(outputDir, sgi.ServiceName) - log.Printf("Creating dir: %v", dir) - if err := os.MkdirAll(dir, 0744); err != nil { +func generate(log Logger, si ServiceItem, basePath, outputDir, chart string) error { + outputPath := path.Join(basePath, defaultOutputDir, outputDir, si.ServiceName) + chartPath := path.Join(basePath, defaultChartDir, chart) + + log.Printf("Entering dir: %v", path.Join(basePath, defaultOutputDir, outputDir)) + log.Printf("Creating dir: %q", si.ServiceName) + if err := os.MkdirAll(outputPath, 0744); err != nil { return fmt.Errorf("could not create output dir: %w", err) } - log.Printf("Copying %v to %v", chartPath, dir) + log.Printf("Copying chart: %v to dir: %q", chart, si.ServiceName) err := filepath.Walk(chartPath, func(source string, info os.FileInfo, err error) error { if err != nil { return err @@ -54,7 +64,7 @@ func generate(log Logger, sgi ServiceGroupItem, outputDir, chartPath string) err return nil } - destination := path.Join(dir, info.Name()) + destination := path.Join(outputPath, info.Name()) if err := copy.Copy(source, destination); err != nil { return err } @@ -67,7 +77,8 @@ func generate(log Logger, sgi ServiceGroupItem, outputDir, chartPath string) err return fmt.Errorf("could not copy template files: %w", err) } - err = generateYaml(log, path.Join(dir, defaultOverridesFile), sgi.Overrides) + log.Printf("Generating yaml file: %q", path.Join(si.ServiceName, defaultOverridesFile)) + err = generateYaml(log, path.Join(outputPath, defaultOverridesFile), si.Overrides) if err != nil { return fmt.Errorf("could not generate %v: %w", defaultOverridesFile, err) } @@ -97,10 +108,10 @@ func helmInstall(log Logger, s Service, namespace, outputDir, tag string) (strin return output, nil } -func helmUninstall(log Logger, sgi ServiceGroupItem) (string, error) { - log.Printf("Running helm uninstall for service: %v", sgi.ServiceName) +func helmUninstall(log Logger, si ServiceItem) (string, error) { + log.Printf("Running helm uninstall for service: %v", si.ServiceName) cmdArgs := []string{ - "uninstall", "-n", sgi.namespace, sgi.ServiceName, + "uninstall", "-n", si.namespace, si.ServiceName, } cmd := exec.Command("helm", cmdArgs...) @@ -108,54 +119,59 @@ func helmUninstall(log Logger, sgi ServiceGroupItem) (string, error) { output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return "", fmt.Errorf("could not uninstall a helm chart for namespace: %v, service group: %v, output: \n%v", sgi.namespace, sgi.serviceGroup, output) + return "", fmt.Errorf("could not uninstall a helm chart for namespace: %v, service group: %v, output: \n%v", si.namespace, si.group, output) } return output, nil } -func (c Configurd) Install(log Logger, sgis []ServiceGroupItem, deploymentDir, tag string) (string, error) { - outputDir := path.Join(defaultOutputDir, deploymentDir) +func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, tag string, verbose bool) (string, error) { + log.Printf("Installing services") + wordkDir := path.Join(basePath, defaultOutputDir) + outputPath := path.Join(basePath, defaultOutputDir, outputDir) - log.Printf("Removing dir: %v", outputDir) - if err := os.RemoveAll(outputDir); err != nil { + log.Printf("Entering dir: %q", wordkDir) + log.Printf("Removing dir: %q", outputDir) + if err := os.RemoveAll(outputPath); err != nil { return "", fmt.Errorf("could not clean workdir directory: %w", err) } - log.Printf("Creating dir: %v", outputDir) - if err := os.MkdirAll(outputDir, 0744); err != nil { + log.Printf("Creating dir: %q", outputDir) + if err := os.MkdirAll(outputPath, 0744); err != nil { return "", fmt.Errorf("could not create a workdir directory: %w", err) } - for _, sgi := range sgis { - err := generate(log, sgi, outputDir, path.Join(defaultChartDir, sgi.chart)) + for _, si := range sis { + err := generate(log, si, basePath, outputDir, si.chart) if err != nil { return "", err } } var output []string - for _, sgi := range sgis { - s, err := c.Service(sgi.ServiceName) + for _, si := range sis { + s, err := c.Service(si.ServiceName) if err != nil { return "", err } - out, err := helmInstall(log, s, sgi.namespace, outputDir, tag) + out, err := helmInstall(log, s, si.namespace, outputPath, tag) if err != nil { return "", err } + logOutput(log, verbose, out) output = append(output, out) } return strings.Join(output, "\n"), nil } -func (c Configurd) Uninstall(log Logger, sgis []ServiceGroupItem) (string, error) { - var output []string - for _, sgi := range sgis { - out, err := helmUninstall(log, sgi) +func (c Configurd) Uninstall(log Logger, sis []ServiceItem, verbose bool) (string, error) { + var outputs []string + for _, si := range sis { + out, err := helmUninstall(log, si) if err != nil { return "", err } - output = append(output, out) + outputs = append(outputs, out) + logOutput(log, verbose, out) } - return strings.Join(output, "\n"), nil + return strings.Join(outputs, "\n"), nil } From 73b2113c816540127c7a5ada192e3e4f27fc3290 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Thu, 12 Mar 2020 11:25:13 +0100 Subject: [PATCH 14/18] Refactor group loading and tidy up commands --- cmd/actions/build.go | 10 +-- cmd/actions/install.go | 8 +- cmd/actions/root.go | 50 ++++++------ cmd/actions/uninstall.go | 6 +- configurd.go | 79 ++++++++++--------- .../namespaces/local/hello-group.yml | 18 +++-- helm.go | 15 ++-- 7 files changed, 88 insertions(+), 98 deletions(-) diff --git a/cmd/actions/build.go b/cmd/actions/build.go index eb850e3..c970417 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -6,26 +6,22 @@ import ( "github.com/spf13/cobra" ) -var cmdBuild = &cobra.Command{ +var buildCmd = &cobra.Command{ Use: "build [SERVICE] -t {TAG}", Short: "Build a service with a given tag", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - _, err := Build(args[0], FlagTag, FlagDir, FlagVerbose) + _, err := Build(args[0], flagTag, flagDir, flagVerbose) if err != nil { log.Fatal(err) } }, } -func init() { - rootCmd.AddCommand(cmdBuild) -} - func Build(service, tag, dir string, flagVerbose bool) (string, error) { svc, err := cnf.Service(service) if err != nil { - return "", fmt.Errorf("service not found: %v", err) + return "", fmt.Errorf("service not found: %v", service) } output, err := svc.RunBuild(log, dir, tag, flagVerbose) if err != nil { diff --git a/cmd/actions/install.go b/cmd/actions/install.go index 1001916..7395da7 100644 --- a/cmd/actions/install.go +++ b/cmd/actions/install.go @@ -7,11 +7,9 @@ import ( ) func init() { - rootCmd.AddCommand(installCmd) - installCmd.Flags().BoolVarP(&flagBuild, "build", "", false, "Build service group before publishing") + installCmd.Flags().BoolVarP(&flagBuild, "build", "b", false, "Build service group before publishing") installCmd.Flags().StringVarP(&flagOutputDir, "output", "o", "default", "Specifies output directory") - installCmd.Flags().StringVarP(&FlagNamespace, "namespace", "n", "default", "Specifies the namespace") - // installCmd.Flags().StringVarP(&FlagGroup, "group", "g", "", "Specifies the group") + installCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "Specifies the namespace") } var ( @@ -26,7 +24,7 @@ var ( Long: "installs the specified group with given namespace and tag version", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - _, err := install(args[0], FlagNamespace, FlagTag, FlagDir, flagOutputDir, flagBuild, FlagVerbose) + _, err := install(args[0], flagNamespace, flagTag, flagDir, flagOutputDir, flagBuild, flagVerbose) if err != nil { log.Fatal(err) } diff --git a/cmd/actions/root.go b/cmd/actions/root.go index bebd001..eb81637 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -2,6 +2,7 @@ package actions import ( "os" + "path" "github.com/foomo/configurd" "github.com/sirupsen/logrus" @@ -12,38 +13,35 @@ var ( log = logrus.New() cnf configurd.Configurd rootCmd = &cobra.Command{ - Use: "cobra", - Short: "A generator for Cobra based Applications", - Long: `Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, + Use: "configurd", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + wdir, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + if flagDir != "" { + flagDir = path.Join(wdir, flagDir) + } + cnf, err = configurd.New(log, flagDir) + if err != nil { + log.Fatal(err) + } + }, } - FlagTag string - FlagDir string - FlagVerbose bool - FlagNamespace string - // FlagGroup string + flagTag string + flagDir string + flagVerbose bool + flagNamespace string ) func init() { - baseDir, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - rootCmd.PersistentFlags().StringVarP(&FlagTag, "tag", "t", "latest", "Specifies the image tag") - rootCmd.PersistentFlags().StringVarP(&FlagDir, "dir", "d", baseDir, "Specifies working directory") - rootCmd.PersistentFlags().BoolVarP(&FlagVerbose, "verbose", "v", false, "Specifies should command output be displayed") + rootCmd.PersistentFlags().StringVarP(&flagTag, "tag", "t", "latest", "Specifies the image tag") + rootCmd.PersistentFlags().StringVarP(&flagDir, "dir", "d", "", "Specifies working directory") + rootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "Specifies should command output be displayed") + rootCmd.AddCommand(buildCmd, installCmd, uninstallCmd) } func Execute() { - var err error - cnf, err = configurd.New(log, FlagDir) - if err != nil { - log.Fatal(err) - } - - if err := rootCmd.Execute(); err != nil { - log.Fatal(err) - } + rootCmd.Execute() } diff --git a/cmd/actions/uninstall.go b/cmd/actions/uninstall.go index 82ef94b..3d5da05 100644 --- a/cmd/actions/uninstall.go +++ b/cmd/actions/uninstall.go @@ -7,9 +7,7 @@ import ( ) func init() { - rootCmd.AddCommand(uninstallCmd) - uninstallCmd.Flags().StringVarP(&FlagNamespace, "namespace", "n", "default", "Specifies the namespace") - // uninstallCmd.Flags().StringVarP(&FlagGroup, "group", "g", "", "Specifies the group") + uninstallCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "Specifies the namespace") } var ( @@ -19,7 +17,7 @@ var ( Long: "uninstalls the specified group with given namespace and tag version", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - _, err := uninstall(args[0], FlagNamespace, FlagVerbose) + _, err := uninstall(args[0], flagNamespace, flagVerbose) if err != nil { log.Fatal(err) } diff --git a/configurd.go b/configurd.go index 0416cc5..37c8aea 100644 --- a/configurd.go +++ b/configurd.go @@ -27,8 +27,8 @@ var ( type Group struct { name string - Services []ServiceItem - Jobs []JobItem + Services map[string]ServiceItem + Jobs map[string]JobItem } type Namespace struct { @@ -53,12 +53,12 @@ func relativePath(absoltePath, basePath string) string { return strings.Replace(absoltePath, basePath+"/", "", -1) } -func New(log Logger, dir string) (Configurd, error) { +func New(log Logger, basePath string) (Configurd, error) { log.Printf("Parsing configuration files") + log.Printf("Entering dir: %q", basePath) c := Configurd{} - serviceDir := path.Join(dir, defaultServiceDir) - log.Printf("Entering dir: %q", serviceDir) + serviceDir := path.Join(basePath, defaultServiceDir) err := filepath.Walk(serviceDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -71,7 +71,7 @@ func New(log Logger, dir string) (Configurd, error) { defer file.Close() var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] - log.Printf("Loading service: %v, from file: %q", name, relativePath(path, serviceDir)) + log.Printf("Loading service: %v, from: %q", name, relativePath(path, basePath)) svc, err := loadService(file, name) if err != nil { return err @@ -85,7 +85,7 @@ func New(log Logger, dir string) (Configurd, error) { return Configurd{}, err } - c.Namespaces, err = loadNamespaces(log, c.Service, dir) + c.Namespaces, err = loadNamespaces(log, c.Service, basePath) if err != nil { return Configurd{}, err @@ -97,20 +97,19 @@ func New(log Logger, dir string) (Configurd, error) { func loadNamespaces(log Logger, sl serviceLoader, basePath string) ([]Namespace, error) { var nss []Namespace namespaceDir := path.Join(basePath, defaultNamespaceDir) - log.Printf("Entering dir: %q", namespaceDir) err := filepath.Walk(namespaceDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() && path != namespaceDir { - log.Printf("Loading namespace: %v, from dir: %q", info.Name(), relativePath(path, namespaceDir)) - sgs, err := loadGroups(log, sl, basePath, info.Name()) + log.Printf("Loading namespace: %v, from: %q", info.Name(), relativePath(path, basePath)) + gs, err := loadGroups(log, sl, basePath, info.Name()) if err != nil { return err } ns := Namespace{ name: info.Name(), - groups: sgs, + groups: gs, } nss = append(nss, ns) } @@ -120,58 +119,58 @@ func loadNamespaces(log Logger, sl serviceLoader, basePath string) ([]Namespace, } func loadGroups(log Logger, sl serviceLoader, basePath, namespace string) ([]Group, error) { - var sgs []Group - - groupDir := path.Join(basePath, defaultNamespaceDir, namespace) - log.Printf("Entering dir: %q", groupDir) - err := filepath.Walk(groupDir, func(path string, info os.FileInfo, err error) error { + var gs []Group + groupPath := path.Join(basePath, defaultNamespaceDir, namespace) + err := filepath.Walk(groupPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] - log.Printf("Loading service group: %v, from file: %q", name, relativePath(path, groupDir)) - sis, err := loadServiceItems(log, sl, namespace, name) + log.Printf("Loading group: %v, from: %q", name, relativePath(path, basePath)) + g, err := loadGroup(log, sl, path, namespace, name) if err != nil { return err } - sg := Group{ - name: name, - Services: sis, - } - sgs = append(sgs, sg) + g.name = name + gs = append(gs, g) } return nil }) - return sgs, err + return gs, err } -func loadServiceItems(log Logger, sl serviceLoader, namespace, group string) ([]ServiceItem, error) { - path := path.Join(defaultNamespaceDir, namespace, group+defaultConfigFileExt) +func loadGroup(log Logger, sl serviceLoader, path, namespace, group string) (Group, error) { file, err := os.Open(path) if err != nil { - return nil, err + return Group{}, err } defer file.Close() var wrapper struct { - Items []ServiceItem `yaml:"group"` + Group Group `yaml:"group"` } if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil { - return nil, fmt.Errorf("could not decode service group items: %w", err) + return Group{}, fmt.Errorf("could not decode group: %w", err) } - for i, si := range wrapper.Items { - log.Printf("Loading service group item: %v", si.ServiceName) - svc, err := sl(si.ServiceName) + for name := range wrapper.Group.Services { + log.Printf("Loading service group item: %v", name) + svc, err := sl(name) if err != nil { - return nil, err + return Group{}, err } - wrapper.Items[i].chart = svc.Chart - wrapper.Items[i].namespace = namespace - wrapper.Items[i].group = group + wrapper.Group.Services[name] = loadServiceItem(wrapper.Group.Services[name], svc.Name, svc.Chart, namespace, group) } - return wrapper.Items, nil + return wrapper.Group, nil +} + +func loadServiceItem(item ServiceItem, service, chart, namespace, group string) ServiceItem { + item.ServiceName = service + item.chart = chart + item.namespace = namespace + item.group = group + return item } func (c Configurd) Service(name string) (Service, error) { @@ -196,8 +195,10 @@ func (c Configurd) GetServiceItems(namespace, group string) []ServiceItem { func (c Configurd) serviceItems() []ServiceItem { var sis []ServiceItem for _, ns := range c.Namespaces { - for _, sg := range ns.groups { - sis = append(sis, sg.Services...) + for _, g := range ns.groups { + for _, si := range g.Services { + sis = append(sis, si) + } } } return sis diff --git a/example/configurd/namespaces/local/hello-group.yml b/example/configurd/namespaces/local/hello-group.yml index c30bf41..4fb0e0c 100644 --- a/example/configurd/namespaces/local/hello-group.yml +++ b/example/configurd/namespaces/local/hello-group.yml @@ -1,9 +1,11 @@ group: - - service: "hello-service" - overrides: - args: - - "--greeting=hehe" - - service: "hi-service" - overrides: - args: - - "--greeting=hai" \ No newline at end of file + services: + hello-service: + overrides: + args: + - "--greeting=hehe" + hi-service: + overrides: + args: + - "--greeting=hai" + jobs: \ No newline at end of file diff --git a/helm.go b/helm.go index 57f7fc9..66424ec 100644 --- a/helm.go +++ b/helm.go @@ -13,7 +13,7 @@ import ( ) type ServiceItem struct { - ServiceName string `yaml:"service"` + ServiceName string Overrides interface{} namespace string group string @@ -47,15 +47,13 @@ func generateYaml(log Logger, path string, data interface{}) error { func generate(log Logger, si ServiceItem, basePath, outputDir, chart string) error { outputPath := path.Join(basePath, defaultOutputDir, outputDir, si.ServiceName) - chartPath := path.Join(basePath, defaultChartDir, chart) - - log.Printf("Entering dir: %v", path.Join(basePath, defaultOutputDir, outputDir)) - log.Printf("Creating dir: %q", si.ServiceName) + log.Printf("Creating dir: %q", path.Join(outputDir, si.ServiceName)) if err := os.MkdirAll(outputPath, 0744); err != nil { return fmt.Errorf("could not create output dir: %w", err) } - log.Printf("Copying chart: %v to dir: %q", chart, si.ServiceName) + log.Printf("Copying chart: %v to dir: %q", chart, path.Join(outputDir, si.ServiceName)) + chartPath := path.Join(basePath, defaultChartDir, chart) err := filepath.Walk(chartPath, func(source string, info os.FileInfo, err error) error { if err != nil { return err @@ -77,7 +75,7 @@ func generate(log Logger, si ServiceItem, basePath, outputDir, chart string) err return fmt.Errorf("could not copy template files: %w", err) } - log.Printf("Generating yaml file: %q", path.Join(si.ServiceName, defaultOverridesFile)) + log.Printf("Generating yaml file: %q", path.Join(outputDir, si.ServiceName, defaultOverridesFile)) err = generateYaml(log, path.Join(outputPath, defaultOverridesFile), si.Overrides) if err != nil { return fmt.Errorf("could not generate %v: %w", defaultOverridesFile, err) @@ -126,10 +124,9 @@ func helmUninstall(log Logger, si ServiceItem) (string, error) { func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, tag string, verbose bool) (string, error) { log.Printf("Installing services") - wordkDir := path.Join(basePath, defaultOutputDir) outputPath := path.Join(basePath, defaultOutputDir, outputDir) - log.Printf("Entering dir: %q", wordkDir) + log.Printf("Entering dir: %q", path.Join(basePath, defaultOutputDir)) log.Printf("Removing dir: %q", outputDir) if err := os.RemoveAll(outputPath); err != nil { return "", fmt.Errorf("could not clean workdir directory: %w", err) From 542baa7ecffeecf290f462f75c99d52dd2009ccc Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 31 Mar 2020 01:02:14 +0200 Subject: [PATCH 15/18] Add init command and apply small fixes --- cmd/actions/build.go | 2 +- cmd/actions/init.go | 28 +++++++++++++++++++ cmd/actions/install.go | 7 +---- cmd/actions/root.go | 12 +++++++- cmd/actions/uninstall.go | 2 +- configurd.go | 26 +++++++++++++++-- example/configurd/charts/example/values.yaml | 5 ++-- .../namespaces/local/hello-group.yml | 18 ++++++++++++ 8 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 cmd/actions/init.go diff --git a/cmd/actions/build.go b/cmd/actions/build.go index c970417..27bc04a 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -25,7 +25,7 @@ func Build(service, tag, dir string, flagVerbose bool) (string, error) { } output, err := svc.RunBuild(log, dir, tag, flagVerbose) if err != nil { - return "", fmt.Errorf("could not build service: %v output:\n%v", svc.Name, output) + return "", outputErrorf(output, err, "could not build service: %v", svc.Name) } return output, nil } diff --git a/cmd/actions/init.go b/cmd/actions/init.go new file mode 100644 index 0000000..bc334a4 --- /dev/null +++ b/cmd/actions/init.go @@ -0,0 +1,28 @@ +package actions + +import ( + "path" + + "github.com/foomo/configurd" + "github.com/spf13/cobra" +) + +var initCmd = &cobra.Command{ + Use: "init [NAME]", + Short: "Initialize an example application with configurd", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + _, err := Init(args[0], flagDir, flagVerbose) + if err != nil { + log.Fatal(err) + } + }, +} + +func Init(name, dir string, flagVerbose bool) (string, error) { + output, err := configurd.Init(log, path.Join(dir, name), flagVerbose) + if err != nil { + return "", outputErrorf(output, err, "could not initialize an example configuraiton") + } + return output, nil +} diff --git a/cmd/actions/install.go b/cmd/actions/install.go index 7395da7..13dfc06 100644 --- a/cmd/actions/install.go +++ b/cmd/actions/install.go @@ -48,12 +48,7 @@ func install(group, namespace, tag, workDir, outputDir string, build, verbose bo } output, err := cnf.Install(log, sis, workDir, outputDir, tag, verbose) if err != nil { - // return "", fmt.Errorf("could not install group: %v output:\n%v \nerror: \n%v", group, output, err) - return "", errorf(output, err, "could not install group: %v", group) + return "", outputErrorf(output, err, "could not install group: %v", group) } return output, nil } - -func errorf(output string, err error, format string, args ...interface{}) error { - return fmt.Errorf("%v, error: %v", fmt.Sprintf(format, args...), err) -} diff --git a/cmd/actions/root.go b/cmd/actions/root.go index eb81637..5bdd1cb 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -1,6 +1,7 @@ package actions import ( + "fmt" "os" "path" @@ -21,6 +22,11 @@ var ( } if flagDir != "" { flagDir = path.Join(wdir, flagDir) + } else { + flagDir = wdir + } + if cmd.Name() == "help" || cmd.Name() == "init" { + return } cnf, err = configurd.New(log, flagDir) if err != nil { @@ -39,9 +45,13 @@ func init() { rootCmd.PersistentFlags().StringVarP(&flagTag, "tag", "t", "latest", "Specifies the image tag") rootCmd.PersistentFlags().StringVarP(&flagDir, "dir", "d", "", "Specifies working directory") rootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "Specifies should command output be displayed") - rootCmd.AddCommand(buildCmd, installCmd, uninstallCmd) + rootCmd.AddCommand(buildCmd, installCmd, uninstallCmd, initCmd) } func Execute() { rootCmd.Execute() } + +func outputErrorf(output string, err error, format string, args ...interface{}) error { + return fmt.Errorf("%v, error: %v", fmt.Sprintf(format, args...), err) +} diff --git a/cmd/actions/uninstall.go b/cmd/actions/uninstall.go index 3d5da05..36f08cf 100644 --- a/cmd/actions/uninstall.go +++ b/cmd/actions/uninstall.go @@ -32,7 +32,7 @@ func uninstall(group, namespace string, flagVerbose bool) (string, error) { } output, err := cnf.Uninstall(log, sis, flagVerbose) if err != nil { - return "", fmt.Errorf("could not uninstall service group: %v output:\n%v", group, output) + return "", outputErrorf(output, err, "could not uninstall service group: %v", group) } return output, nil } diff --git a/configurd.go b/configurd.go index 37c8aea..0659623 100644 --- a/configurd.go +++ b/configurd.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "os/exec" "path" "path/filepath" "strings" @@ -19,6 +20,7 @@ const ( defaultChartDir = "configurd/charts" defaultOutputDir = "configurd/.workdir" defaultOverridesFile = "overrides.yaml" + defaultInitUrl = "https://github.com/foomo/configurd.git/branches/feature/helm-charts-deployments/example" ) var ( @@ -49,8 +51,8 @@ type Logger interface { Fatalf(format string, args ...interface{}) } -func relativePath(absoltePath, basePath string) string { - return strings.Replace(absoltePath, basePath+"/", "", -1) +func relativePath(path, basePath string) string { + return strings.Replace(path, basePath+"/", "", -1) } func New(log Logger, basePath string) (Configurd, error) { @@ -185,7 +187,7 @@ func (c Configurd) Service(name string) (Service, error) { func (c Configurd) GetServiceItems(namespace, group string) []ServiceItem { var sis []ServiceItem for _, si := range c.serviceItems() { - if si.namespace == namespace && (si.group == group || group == "") { + if si.namespace == namespace && si.group == group { sis = append(sis, si) } } @@ -220,3 +222,21 @@ func logOutput(log Logger, verbose bool, format string, args ...interface{}) { log.Printf(format, args...) } } + +func Init(log Logger, dir string, flagVerbose bool) (string, error) { + // log.Printf("Creating dir: %q", dir) + + // if err := os.MkdirAll(dir, 0744); err != nil { + // return "", fmt.Errorf("could not create a directory: %w", err) + // } + + log.Printf("Downloading example configuration into dir: %q", dir) + cmd := exec.Command("svn", "export", defaultInitUrl, dir) + + out, err := cmd.CombinedOutput() + output := strings.Replace(string(out), "\n", "\n\t", -1) + if err != nil { + return "", fmt.Errorf("could not download a configurd example, output: \n%v", output) + } + return output, nil +} diff --git a/example/configurd/charts/example/values.yaml b/example/configurd/charts/example/values.yaml index 7eed295..762bae2 100644 --- a/example/configurd/charts/example/values.yaml +++ b/example/configurd/charts/example/values.yaml @@ -20,11 +20,12 @@ service: env: [] ingress: - enabled: true + enabled: false annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - paths: [] + paths: + - "/" hosts: - chart-example.local tls: [] diff --git a/example/configurd/namespaces/local/hello-group.yml b/example/configurd/namespaces/local/hello-group.yml index 4fb0e0c..e84a901 100644 --- a/example/configurd/namespaces/local/hello-group.yml +++ b/example/configurd/namespaces/local/hello-group.yml @@ -4,8 +4,26 @@ group: overrides: args: - "--greeting=hehe" + service: + targetPort: 80 + port: 80 + ingress: + enabled: true + paths: + - /services/hello + hosts: + - localhost hi-service: overrides: args: - "--greeting=hai" + service: + targetPort: 80 + port: 80 + ingress: + enabled: true + paths: + - /services/hi + hosts: + - localhost jobs: \ No newline at end of file From 90700ef09fd8bb7ed8b69fb240f32c80acf851bc Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Tue, 31 Mar 2020 01:02:49 +0200 Subject: [PATCH 16/18] Update README and remove example Makefile --- README.md | 29 ++++++++++++++++++----------- example/Makefile | 14 -------------- 2 files changed, 18 insertions(+), 25 deletions(-) delete mode 100644 example/Makefile diff --git a/README.md b/README.md index 0749b4f..b63af96 100644 --- a/README.md +++ b/README.md @@ -8,34 +8,41 @@ GoDoc -# Configurd +# Configurd Application for managing kubernetes microservice environment +## Quickstart + +```text +# Create a new folder with an example application with configurd: +$ configurd init [NAME] + +$ cd [NAME]/ + +# Run install for predefined group and namespace: +$ configurd install [GROUP] -n [NAMESPACE] +``` ## Structure ```text /configurd /charts (Helm Charts) - /services - /applications + / /services service-a.yaml service-b.yaml /namespaces /local (reserved, local) - service-a.yaml - servicegroup-a.yaml + group-a.yaml + group-b.yaml /node-a (remote) - global.yaml - servicegroup-a.yaml + groub-c.yaml ``` ## Commands ```text -# Builds service from the /services dir -$ configurd build hello-service -# Shows the diff from ${NAMESPACE} ${SERVICE}? -$ configurd deploy ${NAMESPACE} ${SERVICE} --diff +# See: +$ configurd help ``` \ No newline at end of file diff --git a/example/Makefile b/example/Makefile deleted file mode 100644 index 9e6ff59..0000000 --- a/example/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -build: - go run ../cmd/main.go build hello-service -t master - -install: - go run ../cmd/main.go install local hello-group -t master --build - -uninstall: - go run ../cmd/main.go uninstall local -g hello-group - -install-olaf: - go run ../cmd/main.go install olaf-group -n local -t latest --build -v - -uninstall-olaf: - go run ../cmd/main.go uninstall olaf-group -n local From 0a798a312ceb80e3675757ad4e458d7adbdddb97 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Thu, 9 Apr 2020 11:05:26 +0200 Subject: [PATCH 17/18] Add small changes and fixes and refactor chart templates --- docker.go => build.go | 5 +- cmd/actions/install.go | 2 +- cmd/actions/uninstall.go | 2 +- configurd.go | 30 +++----- .../charts/example/templates/NOTES.txt | 21 ------ .../charts/example/templates/deployment.yaml | 22 +++--- .../charts/example/templates/ingress.yaml | 35 ++++------ .../charts/example/templates/service.yaml | 17 ++--- example/configurd/charts/example/values.yaml | 70 ++++++++++++------- .../namespaces/local/hello-group.yml | 8 ++- helm.go | 64 ++++++++--------- 11 files changed, 127 insertions(+), 149 deletions(-) rename docker.go => build.go (82%) delete mode 100644 example/configurd/charts/example/templates/NOTES.txt diff --git a/docker.go b/build.go similarity index 82% rename from docker.go rename to build.go index 33d1ae1..7567a33 100644 --- a/docker.go +++ b/build.go @@ -18,7 +18,10 @@ type Service struct { } func (s Service) RunBuild(log Logger, dir, tag string, verbose bool) (string, error) { - args := append(strings.Split(s.Build.Command, " "), "-t", fmt.Sprintf("%v:%v", s.Build.Image, tag)) + args := strings.Split(s.Build.Command, " ") + if args[0] == "docker" { + args = append(strings.Split(s.Build.Command, " "), "-t", fmt.Sprintf("%v:%v", s.Build.Image, tag)) + } log.Printf("Building service: %v", s.Name) cmd := exec.Command(args[0], args[1:]...) diff --git a/cmd/actions/install.go b/cmd/actions/install.go index 13dfc06..dfdbae6 100644 --- a/cmd/actions/install.go +++ b/cmd/actions/install.go @@ -40,7 +40,7 @@ func install(group, namespace, tag, workDir, outputDir string, build, verbose bo if build { log.Printf("Building services") for _, si := range sis { - _, err := Build(si.ServiceName, tag, workDir, verbose) + _, err := Build(si.Name, tag, workDir, verbose) if err != nil { return "", err } diff --git a/cmd/actions/uninstall.go b/cmd/actions/uninstall.go index 36f08cf..45110ff 100644 --- a/cmd/actions/uninstall.go +++ b/cmd/actions/uninstall.go @@ -30,7 +30,7 @@ func uninstall(group, namespace string, flagVerbose bool) (string, error) { if len(sis) == 0 { return "", fmt.Errorf("could not find any service for namespace: %v and service group: %v", namespace, group) } - output, err := cnf.Uninstall(log, sis, flagVerbose) + output, err := cnf.Uninstall(log, sis, namespace, flagVerbose) if err != nil { return "", outputErrorf(output, err, "could not uninstall service group: %v", group) } diff --git a/configurd.go b/configurd.go index 0659623..da942ed 100644 --- a/configurd.go +++ b/configurd.go @@ -157,21 +157,21 @@ func loadGroup(log Logger, sl serviceLoader, path, namespace, group string) (Gro } for name := range wrapper.Group.Services { - log.Printf("Loading service group item: %v", name) + log.Printf("Loading group item: %v", name) svc, err := sl(name) if err != nil { return Group{}, err } - wrapper.Group.Services[name] = loadServiceItem(wrapper.Group.Services[name], svc.Name, svc.Chart, namespace, group) + wrapper.Group.Services[name] = loadServiceItem(wrapper.Group.Services[name], svc.Name, namespace, group, svc.Chart) } return wrapper.Group, nil } -func loadServiceItem(item ServiceItem, service, chart, namespace, group string) ServiceItem { - item.ServiceName = service - item.chart = chart +func loadServiceItem(item ServiceItem, service, namespace, group, chart string) ServiceItem { + item.Name = service item.namespace = namespace item.group = group + item.chart = chart return item } @@ -185,21 +185,13 @@ func (c Configurd) Service(name string) (Service, error) { } func (c Configurd) GetServiceItems(namespace, group string) []ServiceItem { - var sis []ServiceItem - for _, si := range c.serviceItems() { - if si.namespace == namespace && si.group == group { - sis = append(sis, si) - } - } - return sis -} - -func (c Configurd) serviceItems() []ServiceItem { var sis []ServiceItem for _, ns := range c.Namespaces { for _, g := range ns.groups { for _, si := range g.Services { - sis = append(sis, si) + if (namespace == "" || namespace == ns.name) && (group == "" || group == g.name) { + sis = append(sis, si) + } } } } @@ -224,12 +216,6 @@ func logOutput(log Logger, verbose bool, format string, args ...interface{}) { } func Init(log Logger, dir string, flagVerbose bool) (string, error) { - // log.Printf("Creating dir: %q", dir) - - // if err := os.MkdirAll(dir, 0744); err != nil { - // return "", fmt.Errorf("could not create a directory: %w", err) - // } - log.Printf("Downloading example configuration into dir: %q", dir) cmd := exec.Command("svn", "export", defaultInitUrl, dir) diff --git a/example/configurd/charts/example/templates/NOTES.txt b/example/configurd/charts/example/templates/NOTES.txt deleted file mode 100644 index a68a77e..0000000 --- a/example/configurd/charts/example/templates/NOTES.txt +++ /dev/null @@ -1,21 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range $.Values.ingress.paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "example.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ include "example.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "example.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "example.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/example/configurd/charts/example/templates/deployment.yaml b/example/configurd/charts/example/templates/deployment.yaml index b3f60ab..38be279 100644 --- a/example/configurd/charts/example/templates/deployment.yaml +++ b/example/configurd/charts/example/templates/deployment.yaml @@ -1,27 +1,27 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "example.fullname" . }} + name: "{{ .Release.Name }}-deployment" labels: - app.kubernetes.io/name: {{ include "example.name" . }} - helm.sh/chart: {{ include "example.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Values.group }} app.kubernetes.io/managed-by: {{ .Release.Service }} + helm.sh/chart: "{{ .Release.Name }}-{{ .Release.Revision }}" spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: - app.kubernetes.io/name: {{ include "example.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Values.group }} template: metadata: labels: - app.kubernetes.io/name: {{ include "example.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Values.group }} spec: containers: - - name: {{ .Values.nameOverride }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + - name: {{ .Release.Name }} + image: {{ .Values.image.repository }} imagePullPolicy: {{ .Values.image.pullPolicy }} {{- with .Values.args }} args: @@ -33,7 +33,7 @@ spec: {{- end }} ports: - name: container-port - containerPort: {{ .Values.service.targetPort }} + containerPort: {{ .Values.service.containerPort }} protocol: TCP livenessProbe: tcpSocket: diff --git a/example/configurd/charts/example/templates/ingress.yaml b/example/configurd/charts/example/templates/ingress.yaml index 71c2324..4855f59 100644 --- a/example/configurd/charts/example/templates/ingress.yaml +++ b/example/configurd/charts/example/templates/ingress.yaml @@ -1,41 +1,30 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "example.fullname" . -}} -{{- $ingressPaths := .Values.ingress.paths -}} -{{- $servicePort := .Values.service.port -}} +{{- if and .Values.ingress.host .Values.ingress.path }} apiVersion: extensions/v1beta1 kind: Ingress metadata: - name: {{ $fullName }} + name: "{{ .Release.Name }}-ingress" labels: - app.kubernetes.io/name: {{ include "example.name" . }} - helm.sh/chart: {{ include "example.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Values.group }} app.kubernetes.io/managed-by: {{ .Release.Service }} + helm.sh/chart: "{{ .Release.Name }}-{{ .Release.Revision }}" {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: -{{- if .Values.ingress.tls }} +{{- if .Values.ingress.secretName }} tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} + - {{ .Values.ingress.host }} + secretName: {{ .Values.ingress.secretName }} {{- end }} rules: - {{- range .Values.ingress.hosts }} - - host: {{ . | quote }} + - host: {{ .Values.ingress.host }} http: paths: - {{- range $ingressPaths }} - - path: {{ . }} + - path: {{ .Values.ingress.path }} backend: - serviceName: {{ $fullName }} - servicePort: {{ $servicePort }} - {{- end }} - {{- end }} + serviceName: {{ .Release.Name }} + servicePort: {{ .Values.service.containerPort }} {{- end }} diff --git a/example/configurd/charts/example/templates/service.yaml b/example/configurd/charts/example/templates/service.yaml index ad07067..4318e24 100644 --- a/example/configurd/charts/example/templates/service.yaml +++ b/example/configurd/charts/example/templates/service.yaml @@ -1,19 +1,16 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "example.fullname" . }} + name: {{ .Release.Name }} labels: - app.kubernetes.io/name: {{ include "example.name" . }} - helm.sh/chart: {{ include "example.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/part-of: {{ .Values.group }} app.kubernetes.io/managed-by: {{ .Release.Service }} + helm.sh/chart: "{{ .Release.Name }}-{{ .Release.Revision }}" spec: + selector: + app.kubernetes.io/name: {{ .Release.Name }} type: {{ .Values.service.type }} ports: - - port: {{ .Values.service.port }} - targetPort: {{ .Values.service.targetPort }} + - port: {{ .Values.service.containerPort }} protocol: TCP - name: http - selector: - app.kubernetes.io/name: {{ include "example.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/example/configurd/charts/example/values.yaml b/example/configurd/charts/example/values.yaml index 762bae2..154f468 100644 --- a/example/configurd/charts/example/values.yaml +++ b/example/configurd/charts/example/values.yaml @@ -1,37 +1,59 @@ -# Default values for example. +# Default values for test. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: - repository: foomo/configurd-hello - tag: master - pullPolicy: IfNotPresent # Must be set when working with a local cluster + repository: nginx + pullPolicy: IfNotPresent -nameOverride: "example" -fullnameOverride: "example" +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 service: - type: NodePort + type: ClusterIP port: 80 - targetPort: 3000 + containerPort: 3000 + +group: test env: [] + # - name: TEST + # value: "test" + +args: [] + # - "arg1==one" + ingress: - enabled: false annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - paths: - - "/" - hosts: - - chart-example.local - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + host: "" + path: "" + secretName: "" resources: {} # We usually recommend not to specify default resources and to leave this as a conscious @@ -39,11 +61,11 @@ resources: {} # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: - # cpu: 100m - # memory: 128Mi -# requests: -# cpu: 100m -# memory: 128Mi + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi nodeSelector: {} diff --git a/example/configurd/namespaces/local/hello-group.yml b/example/configurd/namespaces/local/hello-group.yml index e84a901..4fe6e89 100644 --- a/example/configurd/namespaces/local/hello-group.yml +++ b/example/configurd/namespaces/local/hello-group.yml @@ -5,9 +5,11 @@ group: args: - "--greeting=hehe" service: - targetPort: 80 + containerPort: 80 port: 80 ingress: + path: /services/hello + host: localhost enabled: true paths: - /services/hello @@ -18,9 +20,11 @@ group: args: - "--greeting=hai" service: - targetPort: 80 + containerPort: 80 port: 80 ingress: + path: /services/hi + host: localhost enabled: true paths: - /services/hi diff --git a/helm.go b/helm.go index 66424ec..777bfee 100644 --- a/helm.go +++ b/helm.go @@ -13,19 +13,19 @@ import ( ) type ServiceItem struct { - ServiceName string - Overrides interface{} - namespace string - group string - chart string + Name string + Overrides interface{} + namespace string + group string + chart string } type JobItem struct { - ServiceName string `yaml:"service"` - Overrides interface{} - namespace string - group string - chart string + Name string + Overrides interface{} + namespace string + group string + chart string } func generateYaml(log Logger, path string, data interface{}) error { @@ -45,15 +45,15 @@ func generateYaml(log Logger, path string, data interface{}) error { return nil } -func generate(log Logger, si ServiceItem, basePath, outputDir, chart string) error { - outputPath := path.Join(basePath, defaultOutputDir, outputDir, si.ServiceName) - log.Printf("Creating dir: %q", path.Join(outputDir, si.ServiceName)) +func generate(log Logger, si ServiceItem, basePath, outputDir string) error { + outputPath := path.Join(basePath, defaultOutputDir, outputDir, si.Name) + log.Printf("Creating dir: %q", path.Join(outputDir, si.Name)) if err := os.MkdirAll(outputPath, 0744); err != nil { return fmt.Errorf("could not create output dir: %w", err) } - log.Printf("Copying chart: %v to dir: %q", chart, path.Join(outputDir, si.ServiceName)) - chartPath := path.Join(basePath, defaultChartDir, chart) + log.Printf("Copying chart: %v to dir: %q", si.chart, path.Join(outputDir, si.Name)) + chartPath := path.Join(basePath, defaultChartDir, si.chart) err := filepath.Walk(chartPath, func(source string, info os.FileInfo, err error) error { if err != nil { return err @@ -75,7 +75,7 @@ func generate(log Logger, si ServiceItem, basePath, outputDir, chart string) err return fmt.Errorf("could not copy template files: %w", err) } - log.Printf("Generating yaml file: %q", path.Join(outputDir, si.ServiceName, defaultOverridesFile)) + log.Printf("Generating yaml file: %q", path.Join(outputDir, si.Name, defaultOverridesFile)) err = generateYaml(log, path.Join(outputPath, defaultOverridesFile), si.Overrides) if err != nil { return fmt.Errorf("could not generate %v: %w", defaultOverridesFile, err) @@ -83,17 +83,15 @@ func generate(log Logger, si ServiceItem, basePath, outputDir, chart string) err return nil } -func helmInstall(log Logger, s Service, namespace, outputDir, tag string) (string, error) { - log.Printf("Running helm install for service: %v", s.Name) - chartPath := path.Join(outputDir, s.Name) +func helmInstall(log Logger, si ServiceItem, image, tag, outputDir string) (string, error) { + log.Printf("Running helm install for service: %v", si.Name) + chartPath := path.Join(outputDir, si.Name) cmdArgs := []string{ - "install", s.Name, chartPath, + "install", si.Name, chartPath, "-f", path.Join(chartPath, defaultOverridesFile), - "-n", namespace, - "--set", fmt.Sprintf("nameOverride=%v", s.Name), - "--set", fmt.Sprintf("fullnameOverride=%v", s.Name), - "--set", fmt.Sprintf("image.repository=%v", s.Build.Image), - "--set", fmt.Sprintf("image.tag=%v", tag), + "-n", si.namespace, + "--set", fmt.Sprintf("group=%v", si.group), + "--set", fmt.Sprintf("image.repository=%v:%v", image, tag), } cmd := exec.Command("helm", cmdArgs...) @@ -101,15 +99,15 @@ func helmInstall(log Logger, s Service, namespace, outputDir, tag string) (strin output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return "", fmt.Errorf("could not install a helm chart for service %v output: \n%v", s.Name, output) + return "", fmt.Errorf("could not install a helm chart for service %v output: \n%v", si.Name, output) } return output, nil } func helmUninstall(log Logger, si ServiceItem) (string, error) { - log.Printf("Running helm uninstall for service: %v", si.ServiceName) + log.Printf("Running helm uninstall for service: %v", si.Name) cmdArgs := []string{ - "uninstall", "-n", si.namespace, si.ServiceName, + "uninstall", "-n", si.namespace, si.Name, } cmd := exec.Command("helm", cmdArgs...) @@ -117,7 +115,7 @@ func helmUninstall(log Logger, si ServiceItem) (string, error) { output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return "", fmt.Errorf("could not uninstall a helm chart for namespace: %v, service group: %v, output: \n%v", si.namespace, si.group, output) + return "", fmt.Errorf("could not uninstall a helm chart for service: %v, namespace: %v, output: \n%v", si.Name, si.namespace, output) } return output, nil } @@ -137,7 +135,7 @@ func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, t return "", fmt.Errorf("could not create a workdir directory: %w", err) } for _, si := range sis { - err := generate(log, si, basePath, outputDir, si.chart) + err := generate(log, si, basePath, outputDir) if err != nil { return "", err } @@ -145,11 +143,11 @@ func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, t var output []string for _, si := range sis { - s, err := c.Service(si.ServiceName) + s, err := c.Service(si.Name) if err != nil { return "", err } - out, err := helmInstall(log, s, si.namespace, outputPath, tag) + out, err := helmInstall(log, si, s.Build.Image, tag, outputPath) if err != nil { return "", err } @@ -160,7 +158,7 @@ func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, t return strings.Join(output, "\n"), nil } -func (c Configurd) Uninstall(log Logger, sis []ServiceItem, verbose bool) (string, error) { +func (c Configurd) Uninstall(log Logger, sis []ServiceItem, namespace string, verbose bool) (string, error) { var outputs []string for _, si := range sis { out, err := helmUninstall(log, si) From e23fc4caa73dd1154983c34315bb3e3adaf92332 Mon Sep 17 00:00:00 2001 From: Milos Pejanovic Date: Fri, 10 Apr 2020 13:55:42 +0200 Subject: [PATCH 18/18] Add requested changes and fix smaller issues --- build.go | 6 +----- cmd/actions/build.go | 2 +- cmd/actions/init.go | 5 +++-- cmd/actions/install.go | 6 +++--- cmd/actions/root.go | 3 ++- cmd/actions/uninstall.go | 6 +++--- configurd.go | 25 +++++++++++++++++-------- helm.go | 27 ++++++++++----------------- 8 files changed, 40 insertions(+), 40 deletions(-) diff --git a/build.go b/build.go index 7567a33..0b74628 100644 --- a/build.go +++ b/build.go @@ -2,7 +2,6 @@ package configurd import ( "fmt" - "os/exec" "strings" ) @@ -24,11 +23,8 @@ func (s Service) RunBuild(log Logger, dir, tag string, verbose bool) (string, er } log.Printf("Building service: %v", s.Name) - cmd := exec.Command(args[0], args[1:]...) - cmd.Dir = dir + output, err := runCommand(dir, args...) - out, err := cmd.CombinedOutput() - output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { return output, err } diff --git a/cmd/actions/build.go b/cmd/actions/build.go index 27bc04a..8d01e03 100644 --- a/cmd/actions/build.go +++ b/cmd/actions/build.go @@ -25,7 +25,7 @@ func Build(service, tag, dir string, flagVerbose bool) (string, error) { } output, err := svc.RunBuild(log, dir, tag, flagVerbose) if err != nil { - return "", outputErrorf(output, err, "could not build service: %v", svc.Name) + return output, outputErrorf(output, err, "could not build service: %v", svc.Name) } return output, nil } diff --git a/cmd/actions/init.go b/cmd/actions/init.go index bc334a4..422dd14 100644 --- a/cmd/actions/init.go +++ b/cmd/actions/init.go @@ -9,7 +9,8 @@ import ( var initCmd = &cobra.Command{ Use: "init [NAME]", - Short: "Initialize an example application with configurd", + Short: "initializes an example", + Long: "initializes an example project with configurd", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { _, err := Init(args[0], flagDir, flagVerbose) @@ -22,7 +23,7 @@ var initCmd = &cobra.Command{ func Init(name, dir string, flagVerbose bool) (string, error) { output, err := configurd.Init(log, path.Join(dir, name), flagVerbose) if err != nil { - return "", outputErrorf(output, err, "could not initialize an example configuraiton") + return output, outputErrorf(output, err, "could not initialize an example configuraiton") } return output, nil } diff --git a/cmd/actions/install.go b/cmd/actions/install.go index dfdbae6..7679650 100644 --- a/cmd/actions/install.go +++ b/cmd/actions/install.go @@ -20,8 +20,8 @@ var ( var ( installCmd = &cobra.Command{ Use: "install [GROUP] -n {NAMESPACE} -t {TAG}", - Short: "installs the specified group with given namespace and tag version", - Long: "installs the specified group with given namespace and tag version", + Short: "installs a group of services", + Long: "installs a group of services with given namespace and tag version", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { _, err := install(args[0], flagNamespace, flagTag, flagDir, flagOutputDir, flagBuild, flagVerbose) @@ -48,7 +48,7 @@ func install(group, namespace, tag, workDir, outputDir string, build, verbose bo } output, err := cnf.Install(log, sis, workDir, outputDir, tag, verbose) if err != nil { - return "", outputErrorf(output, err, "could not install group: %v", group) + return output, outputErrorf(output, err, "could not install group: %v", group) } return output, nil } diff --git a/cmd/actions/root.go b/cmd/actions/root.go index 5bdd1cb..6fd8939 100644 --- a/cmd/actions/root.go +++ b/cmd/actions/root.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path" + "strings" "github.com/foomo/configurd" "github.com/sirupsen/logrus" @@ -53,5 +54,5 @@ func Execute() { } func outputErrorf(output string, err error, format string, args ...interface{}) error { - return fmt.Errorf("%v, error: %v", fmt.Sprintf(format, args...), err) + return fmt.Errorf("%v\nerror: %v\noutput: %v", fmt.Sprintf(format, args...), err, strings.ReplaceAll(output, "\n", " ")) } diff --git a/cmd/actions/uninstall.go b/cmd/actions/uninstall.go index 45110ff..c8dce79 100644 --- a/cmd/actions/uninstall.go +++ b/cmd/actions/uninstall.go @@ -13,8 +13,8 @@ func init() { var ( uninstallCmd = &cobra.Command{ Use: "uninstall [GROUP] -n {NAMESPACE} -t {TAG}", - Short: "uninstalls the specified group with given namespace and tag version", - Long: "uninstalls the specified group with given namespace and tag version", + Short: "uninstalls a group", + Long: "uninstalls a group with given namespace and tag version", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { _, err := uninstall(args[0], flagNamespace, flagVerbose) @@ -32,7 +32,7 @@ func uninstall(group, namespace string, flagVerbose bool) (string, error) { } output, err := cnf.Uninstall(log, sis, namespace, flagVerbose) if err != nil { - return "", outputErrorf(output, err, "could not uninstall service group: %v", group) + return output, outputErrorf(output, err, "could not uninstall service group: %v", group) } return output, nil } diff --git a/configurd.go b/configurd.go index da942ed..00f0b3e 100644 --- a/configurd.go +++ b/configurd.go @@ -65,14 +65,14 @@ func New(log Logger, basePath string) (Configurd, error) { if err != nil { return err } - if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { + if !info.IsDir() && strings.HasSuffix(path, defaultConfigFileExt) { file, err := os.Open(path) if err != nil { return err } defer file.Close() - var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] + name := strings.TrimSuffix(info.Name(), defaultConfigFileExt) log.Printf("Loading service: %v, from: %q", name, relativePath(path, basePath)) svc, err := loadService(file, name) if err != nil { @@ -128,13 +128,12 @@ func loadGroups(log Logger, sl serviceLoader, basePath, namespace string) ([]Gro return err } if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) { - var name = info.Name()[0 : len(info.Name())-len(filepath.Ext(info.Name()))] + name := strings.TrimSuffix(info.Name(), defaultConfigFileExt) log.Printf("Loading group: %v, from: %q", name, relativePath(path, basePath)) g, err := loadGroup(log, sl, path, namespace, name) if err != nil { return err } - g.name = name gs = append(gs, g) } return nil @@ -164,6 +163,7 @@ func loadGroup(log Logger, sl serviceLoader, path, namespace, group string) (Gro } wrapper.Group.Services[name] = loadServiceItem(wrapper.Group.Services[name], svc.Name, namespace, group, svc.Chart) } + wrapper.Group.name = group return wrapper.Group, nil } @@ -217,12 +217,21 @@ func logOutput(log Logger, verbose bool, format string, args ...interface{}) { func Init(log Logger, dir string, flagVerbose bool) (string, error) { log.Printf("Downloading example configuration into dir: %q", dir) - cmd := exec.Command("svn", "export", defaultInitUrl, dir) + output, err := runCommand("", "svn", "export", defaultInitUrl, dir) - out, err := cmd.CombinedOutput() - output := strings.Replace(string(out), "\n", "\n\t", -1) if err != nil { - return "", fmt.Errorf("could not download a configurd example, output: \n%v", output) + return output, fmt.Errorf("could not download the configurd example") } return output, nil } + +func runCommand(cwd string, command ...string) (string, error) { + cmd := exec.Command(command[0], command[1:]...) + cmd.Dir = cwd + out, err := cmd.CombinedOutput() + output := strings.Replace(string(out), "\n", "\n\t", -1) + if out == nil { + output = err.Error() + } + return output, err +} diff --git a/helm.go b/helm.go index 777bfee..d173a9d 100644 --- a/helm.go +++ b/helm.go @@ -3,7 +3,6 @@ package configurd import ( "fmt" "os" - "os/exec" "path" "path/filepath" "strings" @@ -86,36 +85,30 @@ func generate(log Logger, si ServiceItem, basePath, outputDir string) error { func helmInstall(log Logger, si ServiceItem, image, tag, outputDir string) (string, error) { log.Printf("Running helm install for service: %v", si.Name) chartPath := path.Join(outputDir, si.Name) - cmdArgs := []string{ - "install", si.Name, chartPath, + cmd := []string{ + "helm", "install", si.Name, chartPath, "-f", path.Join(chartPath, defaultOverridesFile), "-n", si.namespace, "--set", fmt.Sprintf("group=%v", si.group), "--set", fmt.Sprintf("image.repository=%v:%v", image, tag), } - cmd := exec.Command("helm", cmdArgs...) - - out, err := cmd.CombinedOutput() - output := strings.Replace(string(out), "\n", "\n\t", -1) + output, err := runCommand("", cmd...) if err != nil { - return "", fmt.Errorf("could not install a helm chart for service %v output: \n%v", si.Name, output) + return output, fmt.Errorf("could not install a helm chart for service %v", si.Name) } return output, nil } func helmUninstall(log Logger, si ServiceItem) (string, error) { log.Printf("Running helm uninstall for service: %v", si.Name) - cmdArgs := []string{ - "uninstall", "-n", si.namespace, si.Name, + cmd := []string{ + "helm", "uninstall", "-n", si.namespace, si.Name, } - cmd := exec.Command("helm", cmdArgs...) - - out, err := cmd.CombinedOutput() - output := strings.Replace(string(out), "\n", "\n\t", -1) + output, err := runCommand("", cmd...) if err != nil { - return "", fmt.Errorf("could not uninstall a helm chart for service: %v, namespace: %v, output: \n%v", si.Name, si.namespace, output) + return output, fmt.Errorf("could not uninstall a helm chart for service: %v, namespace: %v", si.Name, si.namespace) } return output, nil } @@ -149,7 +142,7 @@ func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, t } out, err := helmInstall(log, si, s.Build.Image, tag, outputPath) if err != nil { - return "", err + return out, err } logOutput(log, verbose, out) output = append(output, out) @@ -163,7 +156,7 @@ func (c Configurd) Uninstall(log Logger, sis []ServiceItem, namespace string, ve for _, si := range sis { out, err := helmUninstall(log, si) if err != nil { - return "", err + return out, err } outputs = append(outputs, out) logOutput(log, verbose, out)