Helm Chart Deployments (#6)

feature/helm charts deployments
This commit is contained in:
Stefan Martinov
2020-04-11 17:18:19 +02:00
committed by GitHub
32 changed files with 946 additions and 158 deletions
+2
View File
@@ -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
+19 -12
View File
@@ -8,34 +8,41 @@
<img src="https://godoc.org/github.com/foomo/configurd?status.svg" alt="GoDoc">
</a>
# 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
/templates (Helm Templates)
/services (Charts)
/applications
/charts (Helm Charts)
/<chart name>
/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
```
+33
View File
@@ -0,0 +1,33 @@
package configurd
import (
"fmt"
"strings"
)
type Build struct {
Command string `yaml:"command"`
Image string `yaml:"image"`
}
type Service struct {
Name string
Build Build `yaml:"build"`
Chart string `yaml:"chart"`
}
func (s Service) RunBuild(log Logger, dir, tag string, verbose bool) (string, error) {
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)
output, err := runCommand(dir, args...)
if err != nil {
return output, err
}
logOutput(log, verbose, output)
return output, nil
}
+18 -12
View File
@@ -1,25 +1,31 @@
package actions
import (
"context"
"log"
"fmt"
"github.com/foomo/configurd"
"github.com/spf13/cobra"
)
func Build(dir, service, tag string) {
cnf, err := configurd.New(dir)
if err != nil {
log.Fatal(err)
}
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)
if err != nil {
log.Fatal(err)
}
},
}
func Build(service, tag, dir string, flagVerbose bool) (string, error) {
svc, err := cnf.Service(service)
if err != nil {
log.Fatalf("service not found: %v", err)
return "", fmt.Errorf("service not found: %v", service)
}
output, err := svc.Build(context.Background(), tag)
output, err := svc.RunBuild(log, dir, tag, flagVerbose)
if err != nil {
log.Fatalf("could not build: %v output:\n%v", output, err)
return output, outputErrorf(output, err, "could not build service: %v", svc.Name)
}
log.Print(output)
return output, nil
}
+29
View File
@@ -0,0 +1,29 @@
package actions
import (
"path"
"github.com/foomo/configurd"
"github.com/spf13/cobra"
)
var initCmd = &cobra.Command{
Use: "init [NAME]",
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)
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 output, outputErrorf(output, err, "could not initialize an example configuraiton")
}
return output, nil
}
+54
View File
@@ -0,0 +1,54 @@
package actions
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
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")
}
var (
flagBuild bool
flagOutputDir string
)
var (
installCmd = &cobra.Command{
Use: "install [GROUP] -n {NAMESPACE} -t {TAG}",
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)
if err != nil {
log.Fatal(err)
}
},
}
)
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 build {
log.Printf("Building services")
for _, si := range sis {
_, err := Build(si.Name, tag, workDir, verbose)
if err != nil {
return "", err
}
}
}
output, err := cnf.Install(log, sis, workDir, outputDir, tag, verbose)
if err != nil {
return output, outputErrorf(output, err, "could not install group: %v", group)
}
return output, nil
}
+58
View File
@@ -0,0 +1,58 @@
package actions
import (
"fmt"
"os"
"path"
"strings"
"github.com/foomo/configurd"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
log = logrus.New()
cnf configurd.Configurd
rootCmd = &cobra.Command{
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)
} else {
flagDir = wdir
}
if cmd.Name() == "help" || cmd.Name() == "init" {
return
}
cnf, err = configurd.New(log, flagDir)
if err != nil {
log.Fatal(err)
}
},
}
flagTag string
flagDir string
flagVerbose bool
flagNamespace string
)
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, initCmd)
}
func Execute() {
rootCmd.Execute()
}
func outputErrorf(output string, err error, format string, args ...interface{}) error {
return fmt.Errorf("%v\nerror: %v\noutput: %v", fmt.Sprintf(format, args...), err, strings.ReplaceAll(output, "\n", " "))
}
+38
View File
@@ -0,0 +1,38 @@
package actions
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
uninstallCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "Specifies the namespace")
}
var (
uninstallCmd = &cobra.Command{
Use: "uninstall [GROUP] -n {NAMESPACE} -t {TAG}",
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)
if err != nil {
log.Fatal(err)
}
},
}
)
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, sis, namespace, flagVerbose)
if err != nil {
return output, outputErrorf(output, err, "could not uninstall service group: %v", group)
}
return output, nil
}
+2 -25
View File
@@ -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()
}
+176 -32
View File
@@ -2,25 +2,40 @@ package configurd
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"gopkg.in/yaml.v2"
)
const (
defaultConfigFileExt = ".yml"
defaultServiceDir = "configurd/services"
defaultTemplateServiceDir = "configurd/templates/services"
defaultNamespaceDir = "configurd/namespaces"
defaultConfigFileExt = ".yml"
defaultServiceDir = "configurd/services"
defaultNamespaceDir = "configurd/namespaces"
defaultChartDir = "configurd/charts"
defaultOutputDir = "configurd/.workdir"
defaultOverridesFile = "overrides.yaml"
defaultInitUrl = "https://github.com/foomo/configurd.git/branches/feature/helm-charts-deployments/example"
)
var (
ErrServiceNotFound = errors.New("service not found")
)
type Group struct {
name string
Services map[string]ServiceItem
Jobs map[string]JobItem
}
type Namespace struct {
Name string
name string
groups []Group
}
type Configurd struct {
@@ -29,18 +44,37 @@ type Configurd struct {
Namespaces []Namespace
}
func New(dir string) (Configurd, error) {
c := Configurd{}
type serviceLoader func(string) (Service, error)
serviceDir := path.Join(dir, defaultServiceDir)
type Logger interface {
Printf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
}
func relativePath(path, basePath string) string {
return strings.Replace(path, basePath+"/", "", -1)
}
func New(log Logger, basePath string) (Configurd, error) {
log.Printf("Parsing configuration files")
log.Printf("Entering dir: %q", basePath)
c := Configurd{}
serviceDir := path.Join(basePath, defaultServiceDir)
err := filepath.Walk(serviceDir, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && (strings.HasSuffix(path, defaultConfigFileExt)) {
if err != nil {
return err
}
if !info.IsDir() && strings.HasSuffix(path, defaultConfigFileExt) {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
svc, err := LoadService(file)
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 {
return err
}
@@ -49,32 +83,98 @@ 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
})
if err != nil {
return Configurd{}, err
}
// 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
})
c.Namespaces, err = loadNamespaces(log, c.Service, basePath)
if err != nil {
return Configurd{}, err
}
// Validate Connections
return c, nil
}
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 {
if err != nil {
return err
}
if info.IsDir() && path != namespaceDir {
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: gs,
}
nss = append(nss, ns)
}
return nil
})
return nss, err
}
func loadGroups(log Logger, sl serviceLoader, basePath, namespace string) ([]Group, 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)) {
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
}
gs = append(gs, g)
}
return nil
})
return gs, err
}
func loadGroup(log Logger, sl serviceLoader, path, namespace, group string) (Group, error) {
file, err := os.Open(path)
if err != nil {
return Group{}, err
}
defer file.Close()
var wrapper struct {
Group Group `yaml:"group"`
}
if err := yaml.NewDecoder(file).Decode(&wrapper); err != nil {
return Group{}, fmt.Errorf("could not decode group: %w", err)
}
for name := range wrapper.Group.Services {
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, namespace, group, svc.Chart)
}
wrapper.Group.name = group
return wrapper.Group, nil
}
func loadServiceItem(item ServiceItem, service, namespace, group, chart string) ServiceItem {
item.Name = service
item.namespace = namespace
item.group = group
item.chart = chart
return item
}
func (c Configurd) Service(name string) (Service, error) {
for _, svc := range c.Services {
if svc.Name == name {
@@ -84,10 +184,54 @@ 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
func (c Configurd) GetServiceItems(namespace, group string) []ServiceItem {
var sis []ServiceItem
for _, ns := range c.Namespaces {
for _, g := range ns.groups {
for _, si := range g.Services {
if (namespace == "" || namespace == ns.name) && (group == "" || group == g.name) {
sis = append(sis, si)
}
}
}
}
return true
return sis
}
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
}
func logOutput(log Logger, verbose bool, format string, args ...interface{}) {
if verbose {
log.Printf(format, args...)
}
}
func Init(log Logger, dir string, flagVerbose bool) (string, error) {
log.Printf("Downloading example configuration into dir: %q", dir)
output, err := runCommand("", "svn", "export", defaultInitUrl, dir)
if err != nil {
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
}
-52
View File
@@ -1,52 +0,0 @@
package configurd
import (
"context"
"fmt"
"io"
"os/exec"
"strings"
"gopkg.in/yaml.v2"
)
type Service struct {
Name string `yaml:"name"`
Docker Docker `yaml:"docker"`
}
type Docker struct {
File string `yaml:"file"`
Context string `yaml:"context"`
Options string `yaml:"options"`
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
}
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),
".",
}
cmd := exec.Command("docker", args...)
cmd.Dir = s.Docker.Context
out, err := cmd.CombinedOutput()
output := strings.Replace(string(out), "\n", "\n\t", -1)
if err != nil {
return output, err
}
return output, nil
}
+2
View File
@@ -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")
-2
View File
@@ -1,2 +0,0 @@
build:
go run ../cmd/main.go build hello-service
+6 -4
View File
@@ -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))
}
+1
View File
@@ -0,0 +1 @@
.workdir
@@ -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/
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: An Example Chart for Configurd
name: example
version: 0.1.0
@@ -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 -}}
@@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ .Release.Name }}-deployment"
labels:
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: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Values.group }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ .Release.Name }}
app.kubernetes.io/part-of: {{ .Values.group }}
spec:
containers:
- name: {{ .Release.Name }}
image: {{ .Values.image.repository }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: container-port
containerPort: {{ .Values.service.containerPort }}
protocol: TCP
livenessProbe:
tcpSocket:
port: container-port
readinessProbe:
tcpSocket:
port: container-port
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 }}
@@ -0,0 +1,30 @@
{{- if and .Values.ingress.host .Values.ingress.path }}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "{{ .Release.Name }}-ingress"
labels:
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.secretName }}
tls:
- hosts:
- {{ .Values.ingress.host }}
secretName: {{ .Values.ingress.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: {{ .Values.ingress.path }}
backend:
serviceName: {{ .Release.Name }}
servicePort: {{ .Values.service.containerPort }}
{{- end }}
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
labels:
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.containerPort }}
protocol: TCP
@@ -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
@@ -0,0 +1,74 @@
# Default values for test.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
pullPolicy: IfNotPresent
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: ClusterIP
port: 80
containerPort: 3000
group: test
env: []
# - name: TEST
# value: "test"
args: []
# - "arg1==one"
ingress:
annotations: {}
# 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
# 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: {}
@@ -0,0 +1,33 @@
group:
services:
hello-service:
overrides:
args:
- "--greeting=hehe"
service:
containerPort: 80
port: 80
ingress:
path: /services/hello
host: localhost
enabled: true
paths:
- /services/hello
hosts:
- localhost
hi-service:
overrides:
args:
- "--greeting=hai"
service:
containerPort: 80
port: 80
ingress:
path: /services/hi
host: localhost
enabled: true
paths:
- /services/hi
hosts:
- localhost
jobs:
+3 -5
View File
@@ -1,7 +1,5 @@
service:
name: hello-service
docker:
build:
command: "docker build application/"
image: foomo/configurd-hello
file: Dockerfile
context: application
# options:
chart: example
@@ -0,0 +1,5 @@
service:
build:
command: "docker build application/"
image: foomo/configurd-hi
chart: example
+5 -1
View File
@@ -3,7 +3,11 @@ module github.com/foomo/configurd
go 1.14
require (
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.2.2
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
)
+24 -13
View File
@@ -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=
@@ -14,19 +16,11 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
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/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=
@@ -55,6 +49,7 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
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=
@@ -69,12 +64,14 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
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/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=
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/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
@@ -86,6 +83,7 @@ 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=
@@ -95,7 +93,10 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
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 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=
@@ -110,9 +111,12 @@ 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.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=
@@ -122,6 +126,7 @@ 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/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=
@@ -141,6 +146,9 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
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-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=
@@ -151,10 +159,13 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
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/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/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/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=
+165
View File
@@ -0,0 +1,165 @@
package configurd
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/otiai10/copy"
"gopkg.in/yaml.v2"
)
type ServiceItem struct {
Name string
Overrides interface{}
namespace string
group string
chart string
}
type JobItem struct {
Name string
Overrides interface{}
namespace string
group string
chart string
}
func generateYaml(log Logger, 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(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", 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
}
if chartPath == source {
return nil
}
destination := path.Join(outputPath, info.Name())
if err := copy.Copy(source, destination); 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)
}
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)
}
return nil
}
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)
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),
}
output, err := runCommand("", cmd...)
if err != nil {
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)
cmd := []string{
"helm", "uninstall", "-n", si.namespace, si.Name,
}
output, err := runCommand("", cmd...)
if err != nil {
return output, fmt.Errorf("could not uninstall a helm chart for service: %v, namespace: %v", si.Name, si.namespace)
}
return output, nil
}
func (c Configurd) Install(log Logger, sis []ServiceItem, basePath, outputDir, tag string, verbose bool) (string, error) {
log.Printf("Installing services")
outputPath := path.Join(basePath, defaultOutputDir, outputDir)
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)
}
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 _, si := range sis {
err := generate(log, si, basePath, outputDir)
if err != nil {
return "", err
}
}
var output []string
for _, si := range sis {
s, err := c.Service(si.Name)
if err != nil {
return "", err
}
out, err := helmInstall(log, si, s.Build.Image, tag, outputPath)
if err != nil {
return out, err
}
logOutput(log, verbose, out)
output = append(output, out)
}
return strings.Join(output, "\n"), nil
}
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)
if err != nil {
return out, err
}
outputs = append(outputs, out)
logOutput(log, verbose, out)
}
return strings.Join(outputs, "\n"), nil
}
+19
View File
@@ -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)
}