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 }