docker/swarmkit

View on GitHub
template/getter.go

Summary

Maintainability
A
1 hr
Test Coverage
package template

import (
    "github.com/moby/swarmkit/v2/agent/exec"
    "github.com/moby/swarmkit/v2/api"
    "github.com/pkg/errors"
)

type templatedSecretGetter struct {
    dependencies exec.DependencyGetter
    t            *api.Task
    node         *api.NodeDescription
}

// NewTemplatedSecretGetter returns a SecretGetter that evaluates templates.
func NewTemplatedSecretGetter(dependencies exec.DependencyGetter, t *api.Task, node *api.NodeDescription) exec.SecretGetter {
    return templatedSecretGetter{dependencies: dependencies, t: t, node: node}
}

func (t templatedSecretGetter) Get(secretID string) (*api.Secret, error) {
    if t.dependencies == nil {
        return nil, errors.New("no secret provider available")
    }

    secrets := t.dependencies.Secrets()
    if secrets == nil {
        return nil, errors.New("no secret provider available")
    }

    secret, err := secrets.Get(secretID)
    if err != nil {
        return secret, err
    }

    newSpec, err := ExpandSecretSpec(secret, t.node, t.t, t.dependencies)
    if err != nil {
        return secret, errors.Wrapf(err, "failed to expand templated secret %s", secretID)
    }

    secretCopy := *secret
    secretCopy.Spec = *newSpec
    return &secretCopy, nil
}

// TemplatedConfigGetter is a ConfigGetter with an additional method to expose
// whether a config contains sensitive data.
type TemplatedConfigGetter interface {
    exec.ConfigGetter

    // GetAndFlagSecretData returns the interpolated config, and also
    // returns true if the config has been interpolated with data from a
    // secret. In this case, the config should be handled specially and
    // should not be written to disk.
    GetAndFlagSecretData(configID string) (*api.Config, bool, error)
}

type templatedConfigGetter struct {
    dependencies exec.DependencyGetter
    t            *api.Task
    node         *api.NodeDescription
}

// NewTemplatedConfigGetter returns a ConfigGetter that evaluates templates.
func NewTemplatedConfigGetter(dependencies exec.DependencyGetter, t *api.Task, node *api.NodeDescription) TemplatedConfigGetter {
    return templatedConfigGetter{dependencies: dependencies, t: t, node: node}
}

func (t templatedConfigGetter) Get(configID string) (*api.Config, error) {
    config, _, err := t.GetAndFlagSecretData(configID)
    return config, err
}

func (t templatedConfigGetter) GetAndFlagSecretData(configID string) (*api.Config, bool, error) {
    if t.dependencies == nil {
        return nil, false, errors.New("no config provider available")
    }

    configs := t.dependencies.Configs()
    if configs == nil {
        return nil, false, errors.New("no config provider available")
    }

    config, err := configs.Get(configID)
    if err != nil {
        return config, false, err
    }

    newSpec, sensitive, err := ExpandConfigSpec(config, t.node, t.t, t.dependencies)
    if err != nil {
        return config, false, errors.Wrapf(err, "failed to expand templated config %s", configID)
    }

    configCopy := *config
    configCopy.Spec = *newSpec
    return &configCopy, sensitive, nil
}

type templatedDependencyGetter struct {
    secrets exec.SecretGetter
    configs TemplatedConfigGetter
    volumes exec.VolumeGetter
}

// NewTemplatedDependencyGetter returns a DependencyGetter that evaluates templates.
func NewTemplatedDependencyGetter(dependencies exec.DependencyGetter, t *api.Task, node *api.NodeDescription) exec.DependencyGetter {
    return templatedDependencyGetter{
        secrets: NewTemplatedSecretGetter(dependencies, t, node),
        configs: NewTemplatedConfigGetter(dependencies, t, node),
        volumes: dependencies.Volumes(),
    }
}

func (t templatedDependencyGetter) Secrets() exec.SecretGetter {
    return t.secrets
}

func (t templatedDependencyGetter) Configs() exec.ConfigGetter {
    return t.configs
}

func (t templatedDependencyGetter) Volumes() exec.VolumeGetter {
    // volumes are not templated, but we include that call (and pass it
    // straight through to the underlying getter) in order to fulfill the
    // DependencyGetter interface.
    return t.volumes
}