github.com/ijc/docker-app@v0.6.1-0.20181012090447-c7ca8bc483fb/internal/helm/templateconversion/convert.go (about)

     1  package templateconversion
     2  
     3  import (
     4  	"regexp"
     5  	"sort"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/docker/app/internal/helm/templatetypes"
    10  	v1beta2 "github.com/docker/app/internal/helm/templatev1beta2"
    11  	"github.com/docker/cli/cli/compose/types"
    12  )
    13  
    14  // FromComposeConfig converts a compose.Config into a StackSpec
    15  func FromComposeConfig(c *templatetypes.Config) *v1beta2.StackSpec {
    16  	if c == nil {
    17  		return nil
    18  	}
    19  	serviceConfigs := make([]v1beta2.ServiceConfig, len(c.Services))
    20  	for i, s := range c.Services {
    21  		serviceConfigs[i] = fromComposeServiceConfig(s)
    22  	}
    23  	sort.Slice(serviceConfigs, func(i, j int) bool {
    24  		return strings.Compare(serviceConfigs[i].Name, serviceConfigs[j].Name) < 0
    25  	})
    26  	return &v1beta2.StackSpec{
    27  		Services: serviceConfigs,
    28  		Secrets:  fromComposeSecrets(c.Secrets),
    29  		Configs:  fromComposeConfigs(c.Configs),
    30  	}
    31  }
    32  
    33  func fromComposeSecrets(s map[string]types.SecretConfig) map[string]v1beta2.SecretConfig {
    34  	if s == nil {
    35  		return nil
    36  	}
    37  	m := map[string]v1beta2.SecretConfig{}
    38  	for key, value := range s {
    39  		m[key] = v1beta2.SecretConfig{
    40  			Name: templatetypes.StringTemplate{Value: value.Name},
    41  			File: templatetypes.StringTemplate{Value: value.File},
    42  			External: v1beta2.External{
    43  				Name:     value.External.Name,
    44  				External: value.External.External,
    45  			},
    46  			Labels: value.Labels,
    47  		}
    48  	}
    49  	return m
    50  }
    51  
    52  func fromComposeConfigs(s map[string]types.ConfigObjConfig) map[string]v1beta2.ConfigObjConfig {
    53  	if s == nil {
    54  		return nil
    55  	}
    56  	m := map[string]v1beta2.ConfigObjConfig{}
    57  	for key, value := range s {
    58  		m[key] = v1beta2.ConfigObjConfig{
    59  			Name: templatetypes.StringTemplate{Value: value.Name},
    60  			File: templatetypes.StringTemplate{Value: value.File},
    61  			External: v1beta2.External{
    62  				Name:     value.External.Name,
    63  				External: value.External.External,
    64  			},
    65  			Labels: value.Labels,
    66  		}
    67  	}
    68  	return m
    69  }
    70  
    71  func fromComposeServiceConfig(s templatetypes.ServiceConfig) v1beta2.ServiceConfig {
    72  	var userID *int64
    73  	if s.User.Value != "" {
    74  		numerical, err := strconv.Atoi(s.User.Value)
    75  		if err == nil {
    76  			unixUserID := int64(numerical)
    77  			userID = &unixUserID
    78  		}
    79  	}
    80  	return v1beta2.ServiceConfig{
    81  		Name:    s.Name,
    82  		CapAdd:  s.CapAdd,
    83  		CapDrop: s.CapDrop,
    84  		Command: s.Command,
    85  		Configs: fromComposeServiceConfigs(s.Configs),
    86  		Deploy: v1beta2.DeployConfig{
    87  			Mode:          s.Deploy.Mode,
    88  			Replicas:      s.Deploy.Replicas,
    89  			Labels:        s.Deploy.Labels,
    90  			UpdateConfig:  fromComposeUpdateConfig(s.Deploy.UpdateConfig),
    91  			Resources:     fromComposeResources(s.Deploy.Resources),
    92  			RestartPolicy: fromComposeRestartPolicy(s.Deploy.RestartPolicy),
    93  			Placement:     fromComposePlacement(s.Deploy.Placement),
    94  		},
    95  		Entrypoint:      s.Entrypoint,
    96  		Environment:     s.Environment,
    97  		ExtraHosts:      s.ExtraHosts,
    98  		Hostname:        s.Hostname,
    99  		HealthCheck:     fromComposeHealthcheck(s.HealthCheck),
   100  		Image:           s.Image,
   101  		Ipc:             s.Ipc,
   102  		Labels:          s.Labels,
   103  		Pid:             s.Pid,
   104  		Ports:           fromComposePorts(s.Ports),
   105  		Privileged:      s.Privileged,
   106  		ReadOnly:        s.ReadOnly,
   107  		Secrets:         fromComposeServiceSecrets(s.Secrets),
   108  		StdinOpen:       s.StdinOpen,
   109  		StopGracePeriod: s.StopGracePeriod,
   110  		Tmpfs:           s.Tmpfs,
   111  		Tty:             s.Tty,
   112  		User:            userID,
   113  		Volumes:         fromComposeServiceVolumeConfig(s.Volumes),
   114  		WorkingDir:      s.WorkingDir,
   115  	}
   116  }
   117  
   118  func fromComposePorts(ports []templatetypes.ServicePortConfig) []v1beta2.ServicePortConfig {
   119  	if ports == nil {
   120  		return nil
   121  	}
   122  	p := make([]v1beta2.ServicePortConfig, len(ports))
   123  	for i, port := range ports {
   124  		p[i] = v1beta2.ServicePortConfig{
   125  			Mode:      port.Mode,
   126  			Target:    port.Target,
   127  			Published: port.Published,
   128  			Protocol:  port.Protocol,
   129  		}
   130  	}
   131  	return p
   132  }
   133  
   134  func fromComposeServiceSecrets(secrets []templatetypes.ServiceSecretConfig) []v1beta2.ServiceSecretConfig {
   135  	if secrets == nil {
   136  		return nil
   137  	}
   138  	c := make([]v1beta2.ServiceSecretConfig, len(secrets))
   139  	for i, secret := range secrets {
   140  		c[i] = v1beta2.ServiceSecretConfig{
   141  			Source: secret.Source,
   142  			Target: secret.Target,
   143  			UID:    secret.UID,
   144  			Mode:   secret.Mode,
   145  		}
   146  	}
   147  	return c
   148  }
   149  
   150  func fromComposeServiceConfigs(configs []templatetypes.ServiceConfigObjConfig) []v1beta2.ServiceConfigObjConfig {
   151  	if configs == nil {
   152  		return nil
   153  	}
   154  	c := make([]v1beta2.ServiceConfigObjConfig, len(configs))
   155  	for i, config := range configs {
   156  		c[i] = v1beta2.ServiceConfigObjConfig{
   157  			Source: config.Source,
   158  			Target: config.Target,
   159  			UID:    config.UID,
   160  			Mode:   config.Mode,
   161  		}
   162  	}
   163  	return c
   164  }
   165  
   166  func fromComposeHealthcheck(h *templatetypes.HealthCheckConfig) *v1beta2.HealthCheckConfig {
   167  	if h == nil {
   168  		return nil
   169  	}
   170  	return &v1beta2.HealthCheckConfig{
   171  		Test:     h.Test,
   172  		Timeout:  h.Timeout,
   173  		Interval: h.Interval,
   174  		Retries:  h.Retries,
   175  	}
   176  }
   177  
   178  func fromComposePlacement(p types.Placement) v1beta2.Placement {
   179  	return v1beta2.Placement{
   180  		Constraints: fromComposeConstraints(p.Constraints),
   181  	}
   182  }
   183  
   184  var constraintEquals = regexp.MustCompile(`([\w\.]*)\W*(==|!=)\W*([\w\.]*)`)
   185  
   186  const (
   187  	swarmOs          = "node.platform.os"
   188  	swarmArch        = "node.platform.arch"
   189  	swarmHostname    = "node.hostname"
   190  	swarmLabelPrefix = "node.labels."
   191  )
   192  
   193  func fromComposeConstraints(s []string) *v1beta2.Constraints {
   194  	if len(s) == 0 {
   195  		return nil
   196  	}
   197  	constraints := &v1beta2.Constraints{}
   198  	for _, constraint := range s {
   199  		matches := constraintEquals.FindStringSubmatch(constraint)
   200  		if len(matches) == 4 {
   201  			key := matches[1]
   202  			operator := matches[2]
   203  			value := matches[3]
   204  			constraint := &v1beta2.Constraint{
   205  				Operator: operator,
   206  				Value:    value,
   207  			}
   208  			switch {
   209  			case key == swarmOs:
   210  				constraints.OperatingSystem = constraint
   211  			case key == swarmArch:
   212  				constraints.Architecture = constraint
   213  			case key == swarmHostname:
   214  				constraints.Hostname = constraint
   215  			case strings.HasPrefix(key, swarmLabelPrefix):
   216  				if constraints.MatchLabels == nil {
   217  					constraints.MatchLabels = map[string]v1beta2.Constraint{}
   218  				}
   219  				constraints.MatchLabels[strings.TrimPrefix(key, swarmLabelPrefix)] = *constraint
   220  			}
   221  		}
   222  	}
   223  	return constraints
   224  }
   225  
   226  func fromComposeResources(r templatetypes.Resources) v1beta2.Resources {
   227  	return v1beta2.Resources{
   228  		Limits:       fromComposeResourcesResource(r.Limits),
   229  		Reservations: fromComposeResourcesResource(r.Reservations),
   230  	}
   231  }
   232  
   233  func fromComposeResourcesResource(r *templatetypes.Resource) *v1beta2.Resource {
   234  	if r == nil {
   235  		return nil
   236  	}
   237  	return &v1beta2.Resource{
   238  		MemoryBytes: r.MemoryBytes,
   239  		NanoCPUs:    r.NanoCPUs,
   240  	}
   241  }
   242  
   243  func fromComposeUpdateConfig(u *templatetypes.UpdateConfig) *v1beta2.UpdateConfig {
   244  	if u == nil {
   245  		return nil
   246  	}
   247  	return &v1beta2.UpdateConfig{
   248  		Parallelism: u.Parallelism,
   249  	}
   250  }
   251  
   252  func fromComposeRestartPolicy(r *types.RestartPolicy) *v1beta2.RestartPolicy {
   253  	if r == nil {
   254  		return nil
   255  	}
   256  	return &v1beta2.RestartPolicy{
   257  		Condition: r.Condition,
   258  	}
   259  }
   260  
   261  func fromComposeServiceVolumeConfig(vs []templatetypes.ServiceVolumeConfig) []v1beta2.ServiceVolumeConfig {
   262  	if vs == nil {
   263  		return nil
   264  	}
   265  	volumes := []v1beta2.ServiceVolumeConfig{}
   266  	for _, v := range vs {
   267  		volumes = append(volumes, v1beta2.ServiceVolumeConfig{
   268  			Type:     v.Type,
   269  			Source:   v.Source,
   270  			Target:   v.Target,
   271  			ReadOnly: v.ReadOnly,
   272  		})
   273  	}
   274  	return volumes
   275  }