2018-01-31 09:37:14 -05:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
2018-05-07 09:35:20 -04:00
|
|
|
"path/filepath"
|
2018-01-31 09:37:14 -05:00
|
|
|
"sort"
|
|
|
|
|
2019-01-16 06:56:37 -05:00
|
|
|
latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
|
2018-12-20 02:45:35 -05:00
|
|
|
"github.com/docker/compose-on-kubernetes/api/labels"
|
2018-01-31 09:37:14 -05:00
|
|
|
apiv1 "k8s.io/api/core/v1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
)
|
|
|
|
|
2018-06-27 10:41:00 -04:00
|
|
|
// Stack is the main type used by stack commands so they remain independent from kubernetes compose component version.
|
|
|
|
type Stack struct {
|
|
|
|
Name string
|
|
|
|
Namespace string
|
|
|
|
ComposeFile string
|
2019-01-16 06:56:37 -05:00
|
|
|
Spec *latest.StackSpec
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
type childResource interface {
|
|
|
|
setOwner(metav1.OwnerReference) error
|
|
|
|
delete() // does not report error, as if a deletion failed, we want to continue deleting other child resources
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteChildResources(childResources []childResource) {
|
|
|
|
for _, cr := range childResources {
|
|
|
|
cr.delete()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func setChildResourcesOwner(childResources []childResource, owner metav1.OwnerReference) error {
|
|
|
|
for _, cr := range childResources {
|
|
|
|
if err := cr.setOwner(owner); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-31 09:37:14 -05:00
|
|
|
// getServices returns all the stack service names, sorted lexicographically
|
2018-06-27 10:41:00 -04:00
|
|
|
func (s *Stack) getServices() []string {
|
|
|
|
services := make([]string, len(s.Spec.Services))
|
|
|
|
for i, service := range s.Spec.Services {
|
2018-01-31 09:37:14 -05:00
|
|
|
services[i] = service.Name
|
|
|
|
}
|
|
|
|
sort.Strings(services)
|
|
|
|
return services
|
|
|
|
}
|
|
|
|
|
|
|
|
// createFileBasedConfigMaps creates a Kubernetes ConfigMap for each Compose global file-based config.
|
2019-01-11 11:23:44 -05:00
|
|
|
func (s *Stack) createFileBasedConfigMaps(configMaps corev1.ConfigMapInterface) ([]childResource, error) {
|
|
|
|
var resources []childResource
|
2018-06-27 10:41:00 -04:00
|
|
|
for name, config := range s.Spec.Configs {
|
2018-01-31 09:37:14 -05:00
|
|
|
if config.File == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-05-07 09:35:20 -04:00
|
|
|
fileName := filepath.Base(config.File)
|
2018-01-31 09:37:14 -05:00
|
|
|
content, err := ioutil.ReadFile(config.File)
|
|
|
|
if err != nil {
|
2019-01-11 11:23:44 -05:00
|
|
|
return resources, err
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
configMap, err := configMaps.Create(toConfigMap(s.Name, name, fileName, content))
|
|
|
|
if err != nil {
|
|
|
|
return resources, err
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
resources = append(resources, &configMapChildResource{client: configMaps, configMap: configMap})
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
return resources, nil
|
|
|
|
}
|
2018-01-31 09:37:14 -05:00
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
type configMapChildResource struct {
|
|
|
|
client corev1.ConfigMapInterface
|
|
|
|
configMap *apiv1.ConfigMap
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *configMapChildResource) setOwner(ref metav1.OwnerReference) error {
|
|
|
|
r.configMap.OwnerReferences = append(r.configMap.OwnerReferences, ref)
|
|
|
|
_, err := r.client.Update(r.configMap)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *configMapChildResource) delete() {
|
|
|
|
r.client.Delete(r.configMap.Name, nil)
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// toConfigMap converts a Compose Config to a Kube ConfigMap.
|
|
|
|
func toConfigMap(stackName, name, key string, content []byte) *apiv1.ConfigMap {
|
|
|
|
return &apiv1.ConfigMap{
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
Kind: "ConfigMap",
|
|
|
|
APIVersion: "v1",
|
|
|
|
},
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: name,
|
|
|
|
Labels: map[string]string{
|
|
|
|
labels.ForStackName: stackName,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Data: map[string]string{
|
|
|
|
key: string(content),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// createFileBasedSecrets creates a Kubernetes Secret for each Compose global file-based secret.
|
2019-01-11 11:23:44 -05:00
|
|
|
func (s *Stack) createFileBasedSecrets(secrets corev1.SecretInterface) ([]childResource, error) {
|
|
|
|
var resources []childResource
|
2018-06-27 10:41:00 -04:00
|
|
|
for name, secret := range s.Spec.Secrets {
|
2018-01-31 09:37:14 -05:00
|
|
|
if secret.File == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-05-07 09:35:20 -04:00
|
|
|
fileName := filepath.Base(secret.File)
|
2018-01-31 09:37:14 -05:00
|
|
|
content, err := ioutil.ReadFile(secret.File)
|
|
|
|
if err != nil {
|
2019-01-11 11:23:44 -05:00
|
|
|
return resources, err
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
secret, err := secrets.Create(toSecret(s.Name, name, fileName, content))
|
|
|
|
if err != nil {
|
|
|
|
return resources, err
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
resources = append(resources, &secretChildResource{client: secrets, secret: secret})
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
2019-01-11 11:23:44 -05:00
|
|
|
return resources, nil
|
|
|
|
}
|
2018-01-31 09:37:14 -05:00
|
|
|
|
2019-01-11 11:23:44 -05:00
|
|
|
type secretChildResource struct {
|
|
|
|
client corev1.SecretInterface
|
|
|
|
secret *apiv1.Secret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *secretChildResource) setOwner(ref metav1.OwnerReference) error {
|
|
|
|
r.secret.OwnerReferences = append(r.secret.OwnerReferences, ref)
|
|
|
|
_, err := r.client.Update(r.secret)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *secretChildResource) delete() {
|
|
|
|
r.client.Delete(r.secret.Name, nil)
|
2018-01-31 09:37:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// toSecret converts a Compose Secret to a Kube Secret.
|
|
|
|
func toSecret(stackName, name, key string, content []byte) *apiv1.Secret {
|
|
|
|
return &apiv1.Secret{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: name,
|
|
|
|
Labels: map[string]string{
|
|
|
|
labels.ForStackName: stackName,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Data: map[string][]byte{
|
|
|
|
key: content,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|