github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/template/expand.go (about)

     1  package template
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/docker/swarmkit/agent/exec"
     8  	"github.com/docker/swarmkit/api"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // ExpandContainerSpec expands templated fields in the runtime using the task
    13  // state and the node where it is scheduled to run.
    14  // Templating is all evaluated on the agent-side, before execution.
    15  //
    16  // Note that these are projected only on runtime values, since active task
    17  // values are typically manipulated in the manager.
    18  func ExpandContainerSpec(n *api.NodeDescription, t *api.Task) (*api.ContainerSpec, error) {
    19  	container := t.Spec.GetContainer()
    20  	if container == nil {
    21  		return nil, errors.Errorf("task missing ContainerSpec to expand")
    22  	}
    23  
    24  	container = container.Copy()
    25  	ctx := NewContext(n, t)
    26  
    27  	var err error
    28  	container.Env, err = expandEnv(ctx, container.Env)
    29  	if err != nil {
    30  		return container, errors.Wrap(err, "expanding env failed")
    31  	}
    32  
    33  	// For now, we only allow templating of string-based mount fields
    34  	container.Mounts, err = expandMounts(ctx, container.Mounts)
    35  	if err != nil {
    36  		return container, errors.Wrap(err, "expanding mounts failed")
    37  	}
    38  
    39  	container.Hostname, err = ctx.Expand(container.Hostname)
    40  	return container, errors.Wrap(err, "expanding hostname failed")
    41  }
    42  
    43  func expandMounts(ctx Context, mounts []api.Mount) ([]api.Mount, error) {
    44  	if len(mounts) == 0 {
    45  		return mounts, nil
    46  	}
    47  
    48  	expanded := make([]api.Mount, len(mounts))
    49  	for i, mount := range mounts {
    50  		var err error
    51  		mount.Source, err = ctx.Expand(mount.Source)
    52  		if err != nil {
    53  			return mounts, errors.Wrapf(err, "expanding mount source %q", mount.Source)
    54  		}
    55  
    56  		mount.Target, err = ctx.Expand(mount.Target)
    57  		if err != nil {
    58  			return mounts, errors.Wrapf(err, "expanding mount target %q", mount.Target)
    59  		}
    60  
    61  		if mount.VolumeOptions != nil {
    62  			mount.VolumeOptions.Labels, err = expandMap(ctx, mount.VolumeOptions.Labels)
    63  			if err != nil {
    64  				return mounts, errors.Wrap(err, "expanding volume labels")
    65  			}
    66  
    67  			if mount.VolumeOptions.DriverConfig != nil {
    68  				mount.VolumeOptions.DriverConfig.Options, err = expandMap(ctx, mount.VolumeOptions.DriverConfig.Options)
    69  				if err != nil {
    70  					return mounts, errors.Wrap(err, "expanding volume driver config")
    71  				}
    72  			}
    73  		}
    74  
    75  		expanded[i] = mount
    76  	}
    77  
    78  	return expanded, nil
    79  }
    80  
    81  func expandMap(ctx Context, m map[string]string) (map[string]string, error) {
    82  	var (
    83  		n   = make(map[string]string, len(m))
    84  		err error
    85  	)
    86  
    87  	for k, v := range m {
    88  		v, err = ctx.Expand(v)
    89  		if err != nil {
    90  			return m, errors.Wrapf(err, "expanding map entry %q=%q", k, v)
    91  		}
    92  
    93  		n[k] = v
    94  	}
    95  
    96  	return n, nil
    97  }
    98  
    99  func expandEnv(ctx Context, values []string) ([]string, error) {
   100  	var result []string
   101  	for _, value := range values {
   102  		var (
   103  			parts = strings.SplitN(value, "=", 2)
   104  			entry = parts[0]
   105  		)
   106  
   107  		if len(parts) > 1 {
   108  			expanded, err := ctx.Expand(parts[1])
   109  			if err != nil {
   110  				return values, errors.Wrapf(err, "expanding env %q", value)
   111  			}
   112  
   113  			entry = fmt.Sprintf("%s=%s", entry, expanded)
   114  		}
   115  
   116  		result = append(result, entry)
   117  	}
   118  
   119  	return result, nil
   120  }
   121  
   122  func expandPayload(ctx *PayloadContext, payload []byte) ([]byte, error) {
   123  	result, err := ctx.Expand(string(payload))
   124  	if err != nil {
   125  		return payload, err
   126  	}
   127  	return []byte(result), nil
   128  }
   129  
   130  // ExpandSecretSpec expands the template inside the secret payload, if any.
   131  // Templating is evaluated on the agent-side.
   132  func ExpandSecretSpec(s *api.Secret, node *api.NodeDescription, t *api.Task, dependencies exec.DependencyGetter) (*api.SecretSpec, error) {
   133  	if s.Spec.Templating == nil {
   134  		return &s.Spec, nil
   135  	}
   136  	if s.Spec.Templating.Name == "golang" {
   137  		ctx := NewPayloadContextFromTask(node, t, dependencies)
   138  		secretSpec := s.Spec.Copy()
   139  
   140  		var err error
   141  		secretSpec.Data, err = expandPayload(&ctx, secretSpec.Data)
   142  		return secretSpec, err
   143  	}
   144  	return &s.Spec, errors.New("unrecognized template type")
   145  }
   146  
   147  // ExpandConfigSpec expands the template inside the config payload, if any.
   148  // Templating is evaluated on the agent-side.
   149  func ExpandConfigSpec(c *api.Config, node *api.NodeDescription, t *api.Task, dependencies exec.DependencyGetter) (*api.ConfigSpec, bool, error) {
   150  	if c.Spec.Templating == nil {
   151  		return &c.Spec, false, nil
   152  	}
   153  	if c.Spec.Templating.Name == "golang" {
   154  		ctx := NewPayloadContextFromTask(node, t, dependencies)
   155  		configSpec := c.Spec.Copy()
   156  
   157  		var err error
   158  		configSpec.Data, err = expandPayload(&ctx, configSpec.Data)
   159  		return configSpec, ctx.sensitive, err
   160  	}
   161  	return &c.Spec, false, errors.New("unrecognized template type")
   162  }