docker/swarmkit

View on GitHub
manager/drivers/secrets.go

Summary

Maintainability
A
35 mins
Test Coverage
package drivers

import (
    "fmt"

    "github.com/moby/swarmkit/v2/api"
    "github.com/moby/swarmkit/v2/api/naming"
    "github.com/moby/swarmkit/v2/node/plugin"
)

const (
    // SecretsProviderAPI is the endpoint for fetching secrets from plugins
    SecretsProviderAPI = "/SecretProvider.GetSecret"

    // SecretsProviderCapability is the secrets provider plugin capability identification
    SecretsProviderCapability = "secretprovider"
)

// SecretDriver provides secrets from different stores
type SecretDriver struct {
    plugin plugin.Plugin
}

// NewSecretDriver creates a new driver that provides third party secrets
func NewSecretDriver(plugin plugin.Plugin) *SecretDriver {
    return &SecretDriver{plugin: plugin}
}

// Get gets a secret from the secret provider. The function returns: the secret value;
// a bool indicating whether the value should be reused across different tasks (defaults to false);
// and an error if either the spec or task are nil, if calling the driver returns an error, or if
// the driver returns an error in the payload.
func (d *SecretDriver) Get(spec *api.SecretSpec, task *api.Task) ([]byte, bool, error) {
    if spec == nil {
        return nil, false, fmt.Errorf("secret spec is nil")
    }
    if task == nil {
        return nil, false, fmt.Errorf("task is nil")
    }

    var secretResp SecretsProviderResponse
    secretReq := &SecretsProviderRequest{
        SecretName:    spec.Annotations.Name,
        SecretLabels:  spec.Annotations.Labels,
        ServiceID:     task.ServiceID,
        ServiceName:   task.ServiceAnnotations.Name,
        ServiceLabels: task.ServiceAnnotations.Labels,
        TaskID:        task.ID,
        TaskName:      naming.Task(task),
        TaskImage:     task.Spec.GetContainer().Image,
        NodeID:        task.NodeID,
    }
    container := task.Spec.GetContainer()
    if container != nil {
        secretReq.ServiceHostname = container.Hostname
    }

    if task.Endpoint != nil && task.Endpoint.Spec != nil {
        secretReq.ServiceEndpointSpec = &EndpointSpec{
            Mode: int32(task.Endpoint.Spec.Mode),
        }
        for _, p := range task.Endpoint.Spec.Ports {
            if p == nil {
                continue
            }
            secretReq.ServiceEndpointSpec.Ports =
                append(secretReq.ServiceEndpointSpec.Ports,
                    PortConfig{
                        Name:          p.Name,
                        Protocol:      int32(p.Protocol),
                        PublishedPort: p.PublishedPort,
                        TargetPort:    p.TargetPort,
                        PublishMode:   int32(p.PublishMode),
                    })
        }
    }

    err := d.plugin.Client().Call(SecretsProviderAPI, secretReq, &secretResp)
    if err != nil {
        return nil, false, err
    }
    if secretResp.Err != "" {
        return nil, secretResp.DoNotReuse, fmt.Errorf(secretResp.Err)
    }
    // Assign the secret value
    return secretResp.Value, secretResp.DoNotReuse, nil
}

// SecretsProviderRequest is the secrets provider request.
type SecretsProviderRequest struct {
    SecretName          string            `json:",omitempty"` // SecretName is the name of the secret to request from the plugin
    SecretLabels        map[string]string `json:",omitempty"` // SecretLabels capture environment names and other metadata pertaining to the secret
    ServiceHostname     string            `json:",omitempty"` // ServiceHostname is the hostname of the service, can be used for x509 certificate
    ServiceID           string            `json:",omitempty"` // ServiceID is the name of the service that requested the secret
    ServiceName         string            `json:",omitempty"` // ServiceName is the name of the service that requested the secret
    ServiceLabels       map[string]string `json:",omitempty"` // ServiceLabels capture environment names and other metadata pertaining to the service
    TaskID              string            `json:",omitempty"` // TaskID is the ID of the task that the secret will be assigned to
    TaskName            string            `json:",omitempty"` // TaskName is the name of the task that the secret will be assigned to
    TaskImage           string            `json:",omitempty"` // TaskName is the image of the task that the secret will be assigned to
    NodeID              string            `json:",omitempty"` // NodeID is the ID of the node that the task will be executed on
    ServiceEndpointSpec *EndpointSpec     `json:",omitempty"` // ServiceEndpointSpec holds the specification for endpoints
}

// SecretsProviderResponse is the secrets provider response.
type SecretsProviderResponse struct {
    Value []byte `json:",omitempty"` // Value is the value of the secret
    Err   string `json:",omitempty"` // Err is the error response of the plugin

    // DoNotReuse indicates that the secret returned from this request should
    // only be used for one task, and any further tasks should call the secret
    // driver again.
    DoNotReuse bool `json:",omitempty"`
}

// EndpointSpec represents the spec of an endpoint.
type EndpointSpec struct {
    Mode  int32        `json:",omitempty"`
    Ports []PortConfig `json:",omitempty"`
}

// PortConfig represents the config of a port.
type PortConfig struct {
    Name     string `json:",omitempty"`
    Protocol int32  `json:",omitempty"`
    // TargetPort is the port inside the container
    TargetPort uint32 `json:",omitempty"`
    // PublishedPort is the port on the swarm hosts
    PublishedPort uint32 `json:",omitempty"`
    // PublishMode is the mode in which port is published
    PublishMode int32 `json:",omitempty"`
}