diff --git a/helm/configMap.go b/helm/configAndSecretMap.go similarity index 82% rename from helm/configMap.go rename to helm/configAndSecretMap.go index c2bdb3c..1ae3205 100644 --- a/helm/configMap.go +++ b/helm/configAndSecretMap.go @@ -13,11 +13,13 @@ type InlineConfig interface { Metadata() *Metadata } +// ConfigMap is made to represent a configMap with data. type ConfigMap struct { *K8sBase `yaml:",inline"` Data map[string]string `yaml:"data"` } +// NewConfigMap returns a new initialzed ConfigMap. func NewConfigMap(name string) *ConfigMap { base := NewBase() base.ApiVersion = "v1" @@ -30,10 +32,12 @@ func NewConfigMap(name string) *ConfigMap { } } +// Metadata returns the metadata of the configMap. func (c *ConfigMap) Metadata() *Metadata { return c.K8sBase.Metadata } +// AddEnvFile adds an environment file to the configMap. func (c *ConfigMap) AddEnvFile(file string) error { content, err := ioutil.ReadFile(file) if err != nil { @@ -57,11 +61,13 @@ func (c *ConfigMap) AddEnvFile(file string) error { } +// Secret is made to represent a secret with data. type Secret struct { *K8sBase `yaml:",inline"` Data map[string]string `yaml:"data"` } +// NewSecret returns a new initialzed Secret. func NewSecret(name string) *Secret { base := NewBase() base.ApiVersion = "v1" @@ -74,6 +80,7 @@ func NewSecret(name string) *Secret { } } +// AddEnvFile adds an environment file to the secret. func (s *Secret) AddEnvFile(file string) error { content, err := ioutil.ReadFile(file) if err != nil { @@ -96,6 +103,8 @@ func (s *Secret) AddEnvFile(file string) error { return nil } + +// Metadata returns the metadata of the secret. func (s *Secret) Metadata() *Metadata { return s.K8sBase.Metadata } diff --git a/helm/container.go b/helm/container.go new file mode 100644 index 0000000..06bb567 --- /dev/null +++ b/helm/container.go @@ -0,0 +1,59 @@ +package helm + +import ( + "strings" + + "github.com/compose-spec/compose-go/types" +) + +// ContainerPort represent a port mapping. +type ContainerPort struct { + Name string + ContainerPort int `yaml:"containerPort"` +} + +// Value represent a environment variable with name and value. +type Value struct { + Name string `yaml:"name"` + Value interface{} `yaml:"value"` +} + +// Container represent a container with name, image, and environment variables. It is used in Deployment. +type Container struct { + Name string `yaml:"name,omitempty"` + Image string `yaml:"image"` + Ports []*ContainerPort `yaml:"ports,omitempty"` + Env []Value `yaml:"env,omitempty"` + EnvFrom []map[string]map[string]string `yaml:"envFrom,omitempty"` + Command []string `yaml:"command,omitempty"` + VolumeMounts []interface{} `yaml:"volumeMounts,omitempty"` + LivenessProbe *Probe `yaml:"livenessProbe,omitempty"` +} + +// NewContainer creates a new container with name, image, labels and environment variables. +func NewContainer(name, image string, environment types.MappingWithEquals, labels map[string]string) *Container { + container := &Container{ + Image: image, + Name: name, + Env: make([]Value, len(environment)), + EnvFrom: make([]map[string]map[string]string, 0), + } + + // find bound environment variable to a service + toServices := make([]string, 0) + if bound, ok := labels[LABEL_ENV_SERVICE]; ok { + toServices = strings.Split(bound, ",") + } + + idx := 0 + for n, v := range environment { + for _, name := range toServices { + if name == n { + *v = RELEASE_NAME + "-" + *v + } + } + container.Env[idx] = Value{Name: n, Value: v} + idx++ + } + return container +} diff --git a/helm/deployment.go b/helm/deployment.go index d10b48e..bf5ff8e 100644 --- a/helm/deployment.go +++ b/helm/deployment.go @@ -1,11 +1,5 @@ package helm -import ( - "strings" - - "github.com/compose-spec/compose-go/types" -) - // Deployment is a k8s deployment. type Deployment struct { *K8sBase `yaml:",inline"` @@ -33,100 +27,6 @@ func NewDepSpec() *DepSpec { } } -type Value struct { - Name string `yaml:"name"` - Value interface{} `yaml:"value"` -} - -type ContainerPort struct { - Name string - ContainerPort int `yaml:"containerPort"` -} - -type Container struct { - Name string `yaml:"name,omitempty"` - Image string `yaml:"image"` - Ports []*ContainerPort `yaml:"ports,omitempty"` - Env []Value `yaml:"env,omitempty"` - EnvFrom []map[string]map[string]string `yaml:"envFrom,omitempty"` - Command []string `yaml:"command,omitempty"` - VolumeMounts []interface{} `yaml:"volumeMounts,omitempty"` - LivenessProbe *Probe `yaml:"livenessProbe,omitempty"` -} - -type HttpGet struct { - Path string `yaml:"path"` - Port int `yaml:"port"` -} - -type Exec struct { - Command []string `yaml:"command"` -} - -type TCP struct { - Port int `yaml:"port"` -} - -type Probe struct { - HttpGet *HttpGet `yaml:"httpGet,omitempty"` - Exec *Exec `yaml:"exec,omitempty"` - TCP *TCP `yaml:"tcp,omitempty"` - Period int `yaml:"periodSeconds"` - Success int `yaml:"successThreshold"` - Failure int `yaml:"failureThreshold"` - InitialDelay int `yaml:"initialDelaySeconds"` -} - -// Create a new Probe object that can be apply to HttpProbe or TCPProbe. -func NewProbe(period, initialDelaySeconds, success, failure int) *Probe { - probe := &Probe{ - Period: period, - Success: success, - Failure: failure, - InitialDelay: initialDelaySeconds, - } - - // fix default values from - // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ - if period == 0 { - probe.Period = 10 - } - if success == 0 { - probe.Success = 1 - } - if failure == 0 { - probe.Failure = 3 - } - return probe -} - -func NewContainer(name, image string, environment types.MappingWithEquals, labels map[string]string) *Container { - container := &Container{ - Image: image, - Name: name, - Env: make([]Value, len(environment)), - EnvFrom: make([]map[string]map[string]string, 0), - } - - // find bound environment variable to a service - toServices := make([]string, 0) - if bound, ok := labels[LABEL_ENV_SERVICE]; ok { - toServices = strings.Split(bound, ",") - } - - idx := 0 - for n, v := range environment { - for _, name := range toServices { - if name == n { - *v = RELEASE_NAME + "-" + *v - } - } - container.Env[idx] = Value{Name: n, Value: v} - idx++ - } - return container -} - type PodSpec struct { InitContainers []*Container `yaml:"initContainers,omitempty"` Containers []*Container `yaml:"containers"` diff --git a/helm/ingress.go b/helm/ingress.go index 8301c06..88c8608 100644 --- a/helm/ingress.go +++ b/helm/ingress.go @@ -1,5 +1,6 @@ package helm +// Ingress is the kubernetes ingress object. type Ingress struct { *K8sBase `yaml:",inline"` Spec IngressSpec diff --git a/helm/k8sbase.go b/helm/k8sbase.go new file mode 100644 index 0000000..af2aa58 --- /dev/null +++ b/helm/k8sbase.go @@ -0,0 +1,56 @@ +package helm + +import ( + "crypto/sha1" + "fmt" + "io/ioutil" +) + +// Metadata is the metadata for a kubernetes object. +type Metadata struct { + Name string `yaml:"name,omitempty"` + Labels map[string]string `yaml:"labels"` + Annotations map[string]string `yaml:"annotations,omitempty"` +} + +func NewMetadata() *Metadata { + return &Metadata{ + Name: "", + Labels: make(map[string]string), + Annotations: make(map[string]string), + } +} + +// K8sBase is the base for all kubernetes objects. +type K8sBase struct { + ApiVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Metadata *Metadata `yaml:"metadata"` +} + +// NewBase is a factory for creating a new base object with metadata, labels and annotations set to the default. +func NewBase() *K8sBase { + b := &K8sBase{ + Metadata: NewMetadata(), + } + // add some information of the build + b.Metadata.Labels[K+"/project"] = GetProjectName() + b.Metadata.Labels[K+"/release"] = RELEASE_NAME + b.Metadata.Annotations[K+"/version"] = Version + return b +} + +func (k *K8sBase) BuildSHA(filename string) { + c, _ := ioutil.ReadFile(filename) + //sum := sha256.Sum256(c) + sum := sha1.Sum(c) + k.Metadata.Annotations[K+"/docker-compose-sha1"] = fmt.Sprintf("%x", string(sum[:])) +} + +func (k *K8sBase) Get() string { + return k.Kind +} + +func (k *K8sBase) Name() string { + return k.Metadata.Name +} diff --git a/helm/notes.go b/helm/notes.go index ce3ad2e..54a33ec 100644 --- a/helm/notes.go +++ b/helm/notes.go @@ -10,6 +10,7 @@ Your application is now deployed. This may take a while to be up and responding. __list__ ` +// GenerateNotesFile generates the notes file for the helm chart. func GenerateNotesFile(ingressess map[string]*Ingress) string { list := make([]string, 0) diff --git a/helm/probe.go b/helm/probe.go new file mode 100644 index 0000000..210ae1d --- /dev/null +++ b/helm/probe.go @@ -0,0 +1,51 @@ +package helm + +// Probe is a struct that can be used to create a Liveness or Readiness probe. +type Probe struct { + HttpGet *HttpGet `yaml:"httpGet,omitempty"` + Exec *Exec `yaml:"exec,omitempty"` + TCP *TCP `yaml:"tcp,omitempty"` + Period int `yaml:"periodSeconds"` + Success int `yaml:"successThreshold"` + Failure int `yaml:"failureThreshold"` + InitialDelay int `yaml:"initialDelaySeconds"` +} + +// Create a new Probe object that can be apply to HttpProbe or TCPProbe. +func NewProbe(period, initialDelaySeconds, success, failure int) *Probe { + probe := &Probe{ + Period: period, + Success: success, + Failure: failure, + InitialDelay: initialDelaySeconds, + } + + // fix default values from + // https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + if period == 0 { + probe.Period = 10 + } + if success == 0 { + probe.Success = 1 + } + if failure == 0 { + probe.Failure = 3 + } + return probe +} + +// HttpGet is a Probe configuration to check http health. +type HttpGet struct { + Path string `yaml:"path"` + Port int `yaml:"port"` +} + +// Execis a Probe configuration to check exec health. +type Exec struct { + Command []string `yaml:"command"` +} + +// TCP is a Probe configuration to check tcp health. +type TCP struct { + Port int `yaml:"port"` +} diff --git a/helm/service.go b/helm/service.go index 6080904..2e00ca5 100644 --- a/helm/service.go +++ b/helm/service.go @@ -1,10 +1,12 @@ package helm +// Service is a Kubernetes service. type Service struct { *K8sBase `yaml:",inline"` Spec *ServiceSpec `yaml:"spec"` } +// NewService creates a new initialized service. func NewService(name string) *Service { s := &Service{ K8sBase: NewBase(), @@ -17,12 +19,14 @@ func NewService(name string) *Service { return s } +// ServicePort is a port on a service. type ServicePort struct { Protocol string `yaml:"protocol"` Port int `yaml:"port"` TargetPort int `yaml:"targetPort"` } +// NewServicePort creates a new initialized service port. func NewServicePort(port, target int) *ServicePort { return &ServicePort{ Protocol: "TCP", @@ -31,12 +35,14 @@ func NewServicePort(port, target int) *ServicePort { } } +// ServiceSpec is the spec for a service. type ServiceSpec struct { Selector map[string]string Ports []*ServicePort Type string `yaml:"type,omitempty"` } +// NewServiceSpec creates a new initialized service spec. func NewServiceSpec() *ServiceSpec { return &ServiceSpec{ Selector: make(map[string]string), diff --git a/helm/storage.go b/helm/storage.go index c10ef3b..491748e 100644 --- a/helm/storage.go +++ b/helm/storage.go @@ -1,10 +1,12 @@ package helm +// Storage is a struct for a PersistentVolumeClaim. type Storage struct { *K8sBase `yaml:",inline"` Spec *PVCSpec } +// NewPVC creates a new PersistentVolumeClaim object. func NewPVC(name, storageName string) *Storage { pvc := &Storage{} pvc.K8sBase = NewBase() @@ -24,6 +26,7 @@ func NewPVC(name, storageName string) *Storage { return pvc } +// PVCSpec is a struct for a PersistentVolumeClaim spec. type PVCSpec struct { Resouces map[string]interface{} `yaml:"resources"` AccessModes []string `yaml:"accessModes"` diff --git a/helm/types.go b/helm/types.go index bf4ccfc..0ae082d 100644 --- a/helm/types.go +++ b/helm/types.go @@ -2,9 +2,6 @@ package helm import ( "bytes" - "crypto/sha1" - "fmt" - "io/ioutil" "os" "strings" "text/template" @@ -58,65 +55,28 @@ var ( Version = "1.0" // should be set from main.Version ) +// Kinded represent an object with a kind. type Kinded interface { + + // Get must resturn the kind name. Get() string } +// Signable represents an object with a signature. type Signable interface { + + // BuildSHA must return the signature. BuildSHA(filename string) } +// Named represents an object with a name. type Named interface { + + // Name must return the name of the object (from metadata). Name() string } -type Metadata struct { - Name string `yaml:"name,omitempty"` - Labels map[string]string `yaml:"labels"` - Annotations map[string]string `yaml:"annotations,omitempty"` -} - -func NewMetadata() *Metadata { - return &Metadata{ - Name: "", - Labels: make(map[string]string), - Annotations: make(map[string]string), - } -} - -type K8sBase struct { - ApiVersion string `yaml:"apiVersion"` - Kind string `yaml:"kind"` - Metadata *Metadata `yaml:"metadata"` -} - -func NewBase() *K8sBase { - - b := &K8sBase{ - Metadata: NewMetadata(), - } - // add some information of the build - b.Metadata.Labels[K+"/project"] = GetProjectName() - b.Metadata.Labels[K+"/release"] = RELEASE_NAME - b.Metadata.Annotations[K+"/version"] = Version - return b -} - -func (k *K8sBase) BuildSHA(filename string) { - c, _ := ioutil.ReadFile(filename) - //sum := sha256.Sum256(c) - sum := sha1.Sum(c) - k.Metadata.Annotations[K+"/docker-compose-sha1"] = fmt.Sprintf("%x", string(sum[:])) -} - -func (k *K8sBase) Get() string { - return k.Kind -} - -func (k *K8sBase) Name() string { - return k.Metadata.Name -} - +// GetProjectName returns the name of the project. func GetProjectName() string { if len(Appname) > 0 { return Appname