github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/stack/kubernetes/stack.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"io/ioutil"
     5  	"path/filepath"
     6  	"sort"
     7  
     8  	latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
     9  	"github.com/docker/compose-on-kubernetes/api/labels"
    10  	apiv1 "k8s.io/api/core/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
    13  )
    14  
    15  // Stack is the main type used by stack commands so they remain independent from kubernetes compose component version.
    16  type Stack struct {
    17  	Name        string
    18  	Namespace   string
    19  	ComposeFile string
    20  	Spec        *latest.StackSpec
    21  }
    22  
    23  type childResource interface {
    24  	setOwner(metav1.OwnerReference) error
    25  	delete() // does not report error, as if a deletion failed, we want to continue deleting other child resources
    26  }
    27  
    28  func deleteChildResources(childResources []childResource) {
    29  	for _, cr := range childResources {
    30  		cr.delete()
    31  	}
    32  }
    33  
    34  func setChildResourcesOwner(childResources []childResource, owner metav1.OwnerReference) error {
    35  	for _, cr := range childResources {
    36  		if err := cr.setOwner(owner); err != nil {
    37  			return err
    38  		}
    39  	}
    40  	return nil
    41  }
    42  
    43  // getServices returns all the stack service names, sorted lexicographically
    44  func (s *Stack) getServices() []string {
    45  	services := make([]string, len(s.Spec.Services))
    46  	for i, service := range s.Spec.Services {
    47  		services[i] = service.Name
    48  	}
    49  	sort.Strings(services)
    50  	return services
    51  }
    52  
    53  // createFileBasedConfigMaps creates a Kubernetes ConfigMap for each Compose global file-based config.
    54  func (s *Stack) createFileBasedConfigMaps(configMaps corev1.ConfigMapInterface) ([]childResource, error) {
    55  	var resources []childResource
    56  	for name, config := range s.Spec.Configs {
    57  		if config.File == "" {
    58  			continue
    59  		}
    60  
    61  		fileName := filepath.Base(config.File)
    62  		content, err := ioutil.ReadFile(config.File)
    63  		if err != nil {
    64  			return resources, err
    65  		}
    66  
    67  		configMap, err := configMaps.Create(toConfigMap(s.Name, name, fileName, content))
    68  		if err != nil {
    69  			return resources, err
    70  		}
    71  		resources = append(resources, &configMapChildResource{client: configMaps, configMap: configMap})
    72  	}
    73  	return resources, nil
    74  }
    75  
    76  type configMapChildResource struct {
    77  	client    corev1.ConfigMapInterface
    78  	configMap *apiv1.ConfigMap
    79  }
    80  
    81  func (r *configMapChildResource) setOwner(ref metav1.OwnerReference) error {
    82  	r.configMap.OwnerReferences = append(r.configMap.OwnerReferences, ref)
    83  	_, err := r.client.Update(r.configMap)
    84  	return err
    85  }
    86  
    87  func (r *configMapChildResource) delete() {
    88  	r.client.Delete(r.configMap.Name, nil)
    89  }
    90  
    91  // toConfigMap converts a Compose Config to a Kube ConfigMap.
    92  func toConfigMap(stackName, name, key string, content []byte) *apiv1.ConfigMap {
    93  	return &apiv1.ConfigMap{
    94  		TypeMeta: metav1.TypeMeta{
    95  			Kind:       "ConfigMap",
    96  			APIVersion: "v1",
    97  		},
    98  		ObjectMeta: metav1.ObjectMeta{
    99  			Name: name,
   100  			Labels: map[string]string{
   101  				labels.ForStackName: stackName,
   102  			},
   103  		},
   104  		Data: map[string]string{
   105  			key: string(content),
   106  		},
   107  	}
   108  }
   109  
   110  // createFileBasedSecrets creates a Kubernetes Secret for each Compose global file-based secret.
   111  func (s *Stack) createFileBasedSecrets(secrets corev1.SecretInterface) ([]childResource, error) {
   112  	var resources []childResource
   113  	for name, secret := range s.Spec.Secrets {
   114  		if secret.File == "" {
   115  			continue
   116  		}
   117  
   118  		fileName := filepath.Base(secret.File)
   119  		content, err := ioutil.ReadFile(secret.File)
   120  		if err != nil {
   121  			return resources, err
   122  		}
   123  
   124  		secret, err := secrets.Create(toSecret(s.Name, name, fileName, content))
   125  		if err != nil {
   126  			return resources, err
   127  		}
   128  		resources = append(resources, &secretChildResource{client: secrets, secret: secret})
   129  	}
   130  	return resources, nil
   131  }
   132  
   133  type secretChildResource struct {
   134  	client corev1.SecretInterface
   135  	secret *apiv1.Secret
   136  }
   137  
   138  func (r *secretChildResource) setOwner(ref metav1.OwnerReference) error {
   139  	r.secret.OwnerReferences = append(r.secret.OwnerReferences, ref)
   140  	_, err := r.client.Update(r.secret)
   141  	return err
   142  }
   143  
   144  func (r *secretChildResource) delete() {
   145  	r.client.Delete(r.secret.Name, nil)
   146  }
   147  
   148  // toSecret converts a Compose Secret to a Kube Secret.
   149  func toSecret(stackName, name, key string, content []byte) *apiv1.Secret {
   150  	return &apiv1.Secret{
   151  		ObjectMeta: metav1.ObjectMeta{
   152  			Name: name,
   153  			Labels: map[string]string{
   154  				labels.ForStackName: stackName,
   155  			},
   156  		},
   157  		Data: map[string][]byte{
   158  			key: content,
   159  		},
   160  	}
   161  }