expand.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package template
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/docker/swarmkit/api"
  6. "github.com/pkg/errors"
  7. )
  8. // ExpandContainerSpec expands templated fields in the runtime using the task
  9. // state. Templating is all evaluated on the agent-side, before execution.
  10. //
  11. // Note that these are projected only on runtime values, since active task
  12. // values are typically manipulated in the manager.
  13. func ExpandContainerSpec(t *api.Task) (*api.ContainerSpec, error) {
  14. container := t.Spec.GetContainer()
  15. if container == nil {
  16. return nil, errors.Errorf("task missing ContainerSpec to expand")
  17. }
  18. container = container.Copy()
  19. ctx := NewContextFromTask(t)
  20. var err error
  21. container.Env, err = expandEnv(ctx, container.Env)
  22. if err != nil {
  23. return container, errors.Wrap(err, "expanding env failed")
  24. }
  25. // For now, we only allow templating of string-based mount fields
  26. container.Mounts, err = expandMounts(ctx, container.Mounts)
  27. if err != nil {
  28. return container, errors.Wrap(err, "expanding mounts failed")
  29. }
  30. container.Hostname, err = ctx.Expand(container.Hostname)
  31. return container, errors.Wrap(err, "expanding hostname failed")
  32. }
  33. func expandMounts(ctx Context, mounts []api.Mount) ([]api.Mount, error) {
  34. if len(mounts) == 0 {
  35. return mounts, nil
  36. }
  37. expanded := make([]api.Mount, len(mounts))
  38. for i, mount := range mounts {
  39. var err error
  40. mount.Source, err = ctx.Expand(mount.Source)
  41. if err != nil {
  42. return mounts, errors.Wrapf(err, "expanding mount source %q", mount.Source)
  43. }
  44. mount.Target, err = ctx.Expand(mount.Target)
  45. if err != nil {
  46. return mounts, errors.Wrapf(err, "expanding mount target %q", mount.Target)
  47. }
  48. if mount.VolumeOptions != nil {
  49. mount.VolumeOptions.Labels, err = expandMap(ctx, mount.VolumeOptions.Labels)
  50. if err != nil {
  51. return mounts, errors.Wrap(err, "expanding volume labels")
  52. }
  53. if mount.VolumeOptions.DriverConfig != nil {
  54. mount.VolumeOptions.DriverConfig.Options, err = expandMap(ctx, mount.VolumeOptions.DriverConfig.Options)
  55. if err != nil {
  56. return mounts, errors.Wrap(err, "expanding volume driver config")
  57. }
  58. }
  59. }
  60. expanded[i] = mount
  61. }
  62. return expanded, nil
  63. }
  64. func expandMap(ctx Context, m map[string]string) (map[string]string, error) {
  65. var (
  66. n = make(map[string]string, len(m))
  67. err error
  68. )
  69. for k, v := range m {
  70. v, err = ctx.Expand(v)
  71. if err != nil {
  72. return m, errors.Wrapf(err, "expanding map entry %q=%q", k, v)
  73. }
  74. n[k] = v
  75. }
  76. return n, nil
  77. }
  78. func expandEnv(ctx Context, values []string) ([]string, error) {
  79. var result []string
  80. for _, value := range values {
  81. var (
  82. parts = strings.SplitN(value, "=", 2)
  83. entry = parts[0]
  84. )
  85. if len(parts) > 1 {
  86. expanded, err := ctx.Expand(parts[1])
  87. if err != nil {
  88. return values, errors.Wrapf(err, "expanding env %q", value)
  89. }
  90. entry = fmt.Sprintf("%s=%s", entry, expanded)
  91. }
  92. result = append(result, entry)
  93. }
  94. return result, nil
  95. }