Use compose-go + improvements (#9)
Use compose-go https://github.com/compose-spec/compose-go to make Katenary parsing compose file the official way. Add labels: - `volume-from` (with `same-pod`) to avoid volume repetition - `ignore` to ignore a service - `mapenv` (replaces the `env-to-service`) to map environment to helm variable (as a template string) - `secret-vars` declares variables as secret values More: - Now, environment (as secret vars) are set in values.yaml - Ingress has got annotations in values.yaml - Probes (liveness probe) are improved - fixed code to optimize - many others fixes about path, bad volume check, refactorisation, tests...
This commit is contained in:
@@ -10,30 +10,38 @@ import (
|
||||
// InlineConfig is made to represent a configMap or a secret
|
||||
type InlineConfig interface {
|
||||
AddEnvFile(filename string) error
|
||||
AddEnv(key, val string) error
|
||||
Metadata() *Metadata
|
||||
}
|
||||
|
||||
// ConfigMap is made to represent a configMap with data.
|
||||
type ConfigMap struct {
|
||||
*K8sBase `yaml:",inline"`
|
||||
Data map[string]string `yaml:"data"`
|
||||
}
|
||||
|
||||
func NewConfigMap(name string) *ConfigMap {
|
||||
// NewConfigMap returns a new initialzed ConfigMap.
|
||||
func NewConfigMap(name, path string) *ConfigMap {
|
||||
base := NewBase()
|
||||
base.ApiVersion = "v1"
|
||||
base.Kind = "ConfigMap"
|
||||
base.Metadata.Name = RELEASE_NAME + "-" + name
|
||||
base.Metadata.Name = ReleaseNameTpl + "-" + name
|
||||
base.Metadata.Labels[K+"/component"] = name
|
||||
if path != "" {
|
||||
base.Metadata.Labels[K+"/path"] = path
|
||||
}
|
||||
return &ConfigMap{
|
||||
K8sBase: base,
|
||||
Data: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -52,28 +60,37 @@ func (c *ConfigMap) AddEnvFile(file string) error {
|
||||
}
|
||||
c.Data[parts[0]] = parts[1]
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *ConfigMap) AddEnv(key, val string) error {
|
||||
c.Data[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
// Secret is made to represent a secret with data.
|
||||
type Secret struct {
|
||||
*K8sBase `yaml:",inline"`
|
||||
Data map[string]string `yaml:"data"`
|
||||
}
|
||||
|
||||
func NewSecret(name string) *Secret {
|
||||
// NewSecret returns a new initialzed Secret.
|
||||
func NewSecret(name, path string) *Secret {
|
||||
base := NewBase()
|
||||
base.ApiVersion = "v1"
|
||||
base.Kind = "Secret"
|
||||
base.Metadata.Name = RELEASE_NAME + "-" + name
|
||||
base.Metadata.Name = ReleaseNameTpl + "-" + name
|
||||
base.Metadata.Labels[K+"/component"] = name
|
||||
if path != "" {
|
||||
base.Metadata.Labels[K+"/path"] = path
|
||||
}
|
||||
return &Secret{
|
||||
K8sBase: base,
|
||||
Data: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +113,14 @@ 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
|
||||
}
|
||||
|
||||
// AddEnv adds an environment variable to the secret.
|
||||
func (s *Secret) AddEnv(key, val string) error {
|
||||
s.Data[key] = fmt.Sprintf(`{{ %s | b64enc }}`, val)
|
||||
return nil
|
||||
}
|
65
helm/container.go
Normal file
65
helm/container.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"katenary/logger"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
type EnvValue interface{}
|
||||
|
||||
// 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 EnvValue `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,
|
||||
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, ",")
|
||||
}
|
||||
if len(toServices) > 0 {
|
||||
// warn, it's deprecated now
|
||||
logger.ActivateColors = true
|
||||
logger.Yellowf(
|
||||
"[deprecated] in \"%s\" service: label %s is deprecated and **ignored**, please use %s instead\n"+
|
||||
"e.g.\n"+
|
||||
" labels:\n"+
|
||||
" FOO: {{ .Release.Name }}-fooservice\n",
|
||||
name,
|
||||
LABEL_ENV_SERVICE,
|
||||
LABEL_MAP_ENV,
|
||||
)
|
||||
logger.ActivateColors = false
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
package helm
|
||||
|
||||
import "strings"
|
||||
|
||||
// Deployment is a k8s deployment.
|
||||
type Deployment struct {
|
||||
*K8sBase `yaml:",inline"`
|
||||
@@ -10,7 +8,7 @@ type Deployment struct {
|
||||
|
||||
func NewDeployment(name string) *Deployment {
|
||||
d := &Deployment{K8sBase: NewBase(), Spec: NewDepSpec()}
|
||||
d.K8sBase.Metadata.Name = RELEASE_NAME + "-" + name
|
||||
d.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name
|
||||
d.K8sBase.ApiVersion = "apps/v1"
|
||||
d.K8sBase.Kind = "Deployment"
|
||||
d.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||
@@ -29,86 +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"`
|
||||
}
|
||||
|
||||
func NewProbe(period, initialDelaySeconds, success, failure int) *Probe {
|
||||
return &Probe{
|
||||
Period: period,
|
||||
Success: success,
|
||||
Failure: failure,
|
||||
InitialDelay: initialDelaySeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func NewContainer(name, image string, environment, 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"`
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package helm
|
||||
|
||||
// Ingress is the kubernetes ingress object.
|
||||
type Ingress struct {
|
||||
*K8sBase `yaml:",inline"`
|
||||
Spec IngressSpec
|
||||
@@ -8,7 +9,7 @@ type Ingress struct {
|
||||
func NewIngress(name string) *Ingress {
|
||||
i := &Ingress{}
|
||||
i.K8sBase = NewBase()
|
||||
i.K8sBase.Metadata.Name = RELEASE_NAME + "-" + name
|
||||
i.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name
|
||||
i.K8sBase.Kind = "Ingress"
|
||||
i.ApiVersion = "networking.k8s.io/v1"
|
||||
i.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||
|
73
helm/k8sbase.go
Normal file
73
helm/k8sbase.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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"] = "{{ .Chart.Name }}"
|
||||
b.Metadata.Labels[K+"/release"] = ReleaseNameTpl
|
||||
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[:]))
|
||||
}
|
||||
|
||||
// Get returns the Kind.
|
||||
func (k *K8sBase) Get() string {
|
||||
return k.Kind
|
||||
}
|
||||
|
||||
// Name returns the name of the object from Metadata.
|
||||
func (k *K8sBase) Name() string {
|
||||
return k.Metadata.Name
|
||||
}
|
||||
|
||||
func (k *K8sBase) GetType() string {
|
||||
if n, ok := k.Metadata.Labels[K+"/type"]; ok {
|
||||
return n
|
||||
}
|
||||
return strings.ToLower(k.Kind)
|
||||
}
|
||||
|
||||
func (k *K8sBase) GetPathRessource() string {
|
||||
if p, ok := k.Metadata.Labels[K+"/path"]; ok {
|
||||
return p
|
||||
}
|
||||
return ""
|
||||
}
|
61
helm/labels.go
Normal file
61
helm/labels.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
const ReleaseNameTpl = "{{ .Release.Name }}"
|
||||
const (
|
||||
LABEL_MAP_ENV = K + "/mapenv"
|
||||
LABEL_ENV_SECRET = K + "/secret-envfiles"
|
||||
LABEL_PORT = K + "/ports"
|
||||
LABEL_INGRESS = K + "/ingress"
|
||||
LABEL_VOL_CM = K + "/configmap-volumes"
|
||||
LABEL_HEALTHCHECK = K + "/healthcheck"
|
||||
LABEL_SAMEPOD = K + "/same-pod"
|
||||
LABEL_VOLUMEFROM = K + "/volume-from"
|
||||
LABEL_EMPTYDIRS = K + "/empty-dirs"
|
||||
LABEL_IGNORE = K + "/ignore"
|
||||
LABEL_SECRETVARS = K + "/secret-vars"
|
||||
|
||||
//deprecated: use LABEL_MAP_ENV instead
|
||||
LABEL_ENV_SERVICE = K + "/env-to-service"
|
||||
)
|
||||
|
||||
// GetLabelsDocumentation returns the documentation for the labels.
|
||||
func GetLabelsDocumentation() string {
|
||||
t, _ := template.New("labels").Parse(`
|
||||
# Labels
|
||||
{{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart
|
||||
{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file
|
||||
{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap
|
||||
{{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style)
|
||||
{{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated)
|
||||
{{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated)
|
||||
{{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated)
|
||||
{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name
|
||||
{{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style)
|
||||
{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated)
|
||||
{{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**.
|
||||
{{ printf "%-34s" ""}} You can use these form of label values:
|
||||
{{ printf "%-35s" ""}}- "http://[not used address][:port][/path]" to specify an http healthcheck
|
||||
{{ printf "%-35s" ""}}- "tcp://[not used address]:port" to specify a tcp healthcheck
|
||||
{{ printf "%-35s" ""}}- other string is condidered as a "command" healthcheck
|
||||
`)
|
||||
buff := bytes.NewBuffer(nil)
|
||||
t.Execute(buff, map[string]string{
|
||||
"LABEL_ENV_SECRET": LABEL_ENV_SECRET,
|
||||
"LABEL_PORT": LABEL_PORT,
|
||||
"LABEL_INGRESS": LABEL_INGRESS,
|
||||
"LABEL_VOL_CM": LABEL_VOL_CM,
|
||||
"LABEL_HEALTHCHECK": LABEL_HEALTHCHECK,
|
||||
"LABEL_SAMEPOD": LABEL_SAMEPOD,
|
||||
"LABEL_VOLUMEFROM": LABEL_VOLUMEFROM,
|
||||
"LABEL_EMPTYDIRS": LABEL_EMPTYDIRS,
|
||||
"LABEL_IGNORE": LABEL_IGNORE,
|
||||
"LABEL_MAP_ENV": LABEL_MAP_ENV,
|
||||
"LABEL_SECRETVARS": LABEL_SECRETVARS,
|
||||
})
|
||||
return buff.String()
|
||||
}
|
@@ -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)
|
||||
|
104
helm/probe.go
Normal file
104
helm/probe.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
)
|
||||
|
||||
// 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 float64 `yaml:"periodSeconds"`
|
||||
InitialDelay float64 `yaml:"initialDelaySeconds"`
|
||||
Success uint64 `yaml:"successThreshold"`
|
||||
Failure uint64 `yaml:"failureThreshold"`
|
||||
}
|
||||
|
||||
// Create a new Probe object that can be apply to HttpProbe or TCPProbe.
|
||||
func NewProbe(period, initialDelaySeconds float64, success, failure uint64) *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
|
||||
}
|
||||
|
||||
// NewProbeWithDuration creates a new Probe object with the given duration from types.
|
||||
func NewProbeWithDuration(period, initialDelaySeconds *types.Duration, success, failure *uint64) *Probe {
|
||||
|
||||
if period == nil {
|
||||
d := types.Duration(0 * time.Second)
|
||||
period = &d
|
||||
}
|
||||
|
||||
if initialDelaySeconds == nil {
|
||||
d := types.Duration(0 * time.Second)
|
||||
initialDelaySeconds = &d
|
||||
}
|
||||
|
||||
if success == nil {
|
||||
s := uint64(0)
|
||||
success = &s
|
||||
}
|
||||
|
||||
if failure == nil {
|
||||
f := uint64(0)
|
||||
failure = &f
|
||||
}
|
||||
|
||||
p, err := time.ParseDuration(period.String())
|
||||
if err != nil {
|
||||
p = time.Second * 10
|
||||
}
|
||||
|
||||
i, err := time.ParseDuration(initialDelaySeconds.String())
|
||||
if err != nil {
|
||||
i = time.Second * 0
|
||||
}
|
||||
|
||||
return NewProbe(p.Seconds(), i.Seconds(), *success, *failure)
|
||||
|
||||
}
|
||||
|
||||
// NewProbeFromService creates a new Probe object from a ServiceConfig.
|
||||
func NewProbeFromService(s *types.ServiceConfig) *Probe {
|
||||
if s == nil || s.HealthCheck == nil {
|
||||
return NewProbe(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
return NewProbeWithDuration(s.HealthCheck.Interval, s.HealthCheck.StartPeriod, nil, s.HealthCheck.Retries)
|
||||
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
@@ -1,28 +1,32 @@
|
||||
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(),
|
||||
Spec: NewServiceSpec(),
|
||||
}
|
||||
s.K8sBase.Metadata.Name = RELEASE_NAME + "-" + name
|
||||
s.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name
|
||||
s.K8sBase.Kind = "Service"
|
||||
s.K8sBase.ApiVersion = "v1"
|
||||
s.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||
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),
|
||||
|
@@ -1,17 +1,40 @@
|
||||
package helm
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
made = make(map[string]bool)
|
||||
locker = sync.Mutex{}
|
||||
)
|
||||
|
||||
// ResetMadePVC resets the cache of made PVCs.
|
||||
// Useful in tests only.
|
||||
func ResetMadePVC() {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
made = make(map[string]bool)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
if _, ok := made[name+storageName]; ok {
|
||||
return nil
|
||||
}
|
||||
made[name+storageName] = true
|
||||
pvc := &Storage{}
|
||||
pvc.K8sBase = NewBase()
|
||||
pvc.K8sBase.Kind = "PersistentVolumeClaim"
|
||||
pvc.K8sBase.Metadata.Labels[K+"/pvc-name"] = storageName
|
||||
pvc.K8sBase.ApiVersion = "v1"
|
||||
pvc.K8sBase.Metadata.Name = RELEASE_NAME + "-" + storageName
|
||||
pvc.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + storageName
|
||||
pvc.K8sBase.Metadata.Labels[K+"/component"] = name
|
||||
pvc.Spec = &PVCSpec{
|
||||
Resouces: map[string]interface{}{
|
||||
@@ -24,6 +47,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"`
|
||||
|
102
helm/types.go
102
helm/types.go
@@ -1,122 +1,36 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const K = "katenary.io"
|
||||
const RELEASE_NAME = "{{ .Release.Name }}"
|
||||
const (
|
||||
LABEL_ENV_SECRET = K + "/secret-envfiles"
|
||||
LABEL_PORT = K + "/ports"
|
||||
LABEL_INGRESS = K + "/ingress"
|
||||
LABEL_ENV_SERVICE = K + "/env-to-service"
|
||||
LABEL_VOL_CM = K + "/configmap-volumes"
|
||||
LABEL_HEALTHCHECK = K + "/healthcheck"
|
||||
LABEL_SAMEPOD = K + "/same-pod"
|
||||
LABEL_EMPTYDIRS = K + "/empty-dirs"
|
||||
)
|
||||
|
||||
func GetLabelsDocumentation() string {
|
||||
t, _ := template.New("labels").Parse(`
|
||||
# Labels
|
||||
{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap
|
||||
{{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated)
|
||||
{{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated)
|
||||
{{.LABEL_ENV_SERVICE | printf "%-33s"}}: specifies that the environment variable points on a service name (coma separated)
|
||||
{{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated)
|
||||
{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name
|
||||
{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated)
|
||||
{{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**.
|
||||
{{ printf "%-34s" ""}} You can use these form of label values:
|
||||
{{ printf "%-35s" ""}}- "http://[not used address][:port][/path]" to specify an http healthcheck
|
||||
{{ printf "%-35s" ""}}- "tcp://[not used address]:port" to specify a tcp healthcheck
|
||||
{{ printf "%-35s" ""}}- other string is condidered as a "command" healthcheck
|
||||
`)
|
||||
buff := bytes.NewBuffer(nil)
|
||||
t.Execute(buff, map[string]string{
|
||||
"LABEL_ENV_SECRET": LABEL_ENV_SECRET,
|
||||
"LABEL_ENV_SERVICE": LABEL_ENV_SERVICE,
|
||||
"LABEL_PORT": LABEL_PORT,
|
||||
"LABEL_INGRESS": LABEL_INGRESS,
|
||||
"LABEL_VOL_CM": LABEL_VOL_CM,
|
||||
"LABEL_HEALTHCHECK": LABEL_HEALTHCHECK,
|
||||
"LABEL_SAMEPOD": LABEL_SAMEPOD,
|
||||
"LABEL_EMPTYDIRS": LABEL_EMPTYDIRS,
|
||||
})
|
||||
return buff.String()
|
||||
}
|
||||
|
||||
var (
|
||||
Appname = ""
|
||||
Appname = "" // set at runtime
|
||||
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
|
||||
|
Reference in New Issue
Block a user