mirror of
https://github.com/openshift/source-to-image.git
synced 2026-02-05 12:44:54 +01:00
639 lines
21 KiB
Go
639 lines
21 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/openshift/source-to-image/pkg/scm/git"
|
|
utillog "github.com/openshift/source-to-image/pkg/util/log"
|
|
"github.com/openshift/source-to-image/pkg/util/user"
|
|
)
|
|
|
|
var log = utillog.StderrLog
|
|
|
|
// invalidFilenameCharacters contains a list of character we consider malicious
|
|
// when injecting the directories into containers.
|
|
const invalidFilenameCharacters = `;*?"<>|%#$!+{}&[],"'` + "`"
|
|
|
|
const (
|
|
// PullAlways means that we always attempt to pull the latest image.
|
|
PullAlways PullPolicy = "always"
|
|
|
|
// PullNever means that we never pull an image, but only use a local image.
|
|
PullNever PullPolicy = "never"
|
|
|
|
// PullIfNotPresent means that we pull if the image isn't present on disk.
|
|
PullIfNotPresent PullPolicy = "if-not-present"
|
|
|
|
// DefaultBuilderPullPolicy specifies the default pull policy to use
|
|
DefaultBuilderPullPolicy = PullIfNotPresent
|
|
|
|
// DefaultRuntimeImagePullPolicy specifies the default pull policy to use.
|
|
DefaultRuntimeImagePullPolicy = PullIfNotPresent
|
|
|
|
// DefaultPreviousImagePullPolicy specifies policy for pulling the previously
|
|
// build Docker image when doing incremental build
|
|
DefaultPreviousImagePullPolicy = PullIfNotPresent
|
|
)
|
|
|
|
// Config contains essential fields for performing build.
|
|
type Config struct {
|
|
// DisplayName is a result image display-name label. This defaults to the
|
|
// output image name.
|
|
DisplayName string
|
|
|
|
// Description is a result image description label. The default is no
|
|
// description.
|
|
Description string
|
|
|
|
// BuilderImage describes which image is used for building the result images.
|
|
BuilderImage string
|
|
|
|
// BuilderImageVersion provides optional version information about the builder image.
|
|
BuilderImageVersion string
|
|
|
|
// BuilderBaseImageVersion provides optional version information about the builder base image.
|
|
BuilderBaseImageVersion string
|
|
|
|
// RuntimeImage specifies the image that will be a base for resulting image
|
|
// and will be used for running an application. By default, BuilderImage is
|
|
// used for building and running, but the latter may be overridden.
|
|
RuntimeImage string
|
|
|
|
// RuntimeImagePullPolicy specifies when to pull a runtime image.
|
|
RuntimeImagePullPolicy PullPolicy
|
|
|
|
// RuntimeAuthentication holds the authentication information for pulling the
|
|
// runtime Docker images from private repositories.
|
|
RuntimeAuthentication AuthConfig
|
|
|
|
// RuntimeArtifacts specifies a list of source/destination pairs that will
|
|
// be copied from builder to a runtime image. Source can be a file or
|
|
// directory. Destination must be a directory. Regardless whether it
|
|
// is an absolute or relative path, it will be placed into image's WORKDIR.
|
|
// Destination also can be empty or equals to ".", in this case it just
|
|
// refers to a root of WORKDIR.
|
|
// In case it's empty, S2I will try to get this list from
|
|
// io.openshift.s2i.assemble-input-files label on a RuntimeImage.
|
|
RuntimeArtifacts VolumeList
|
|
|
|
// DockerConfig describes how to access host docker daemon.
|
|
DockerConfig *DockerConfig
|
|
|
|
// DockerCfgPath provides the path to the .dockercfg file
|
|
DockerCfgPath string
|
|
|
|
// PullAuthentication holds the authentication information for pulling the
|
|
// Docker images from private repositories
|
|
PullAuthentication AuthConfig
|
|
|
|
// IncrementalAuthentication holds the authentication information for pulling the
|
|
// previous image from private repositories
|
|
IncrementalAuthentication AuthConfig
|
|
|
|
// DockerNetworkMode is used to set the docker network setting to --net=container:<id>
|
|
// when the builder is invoked from a container.
|
|
DockerNetworkMode DockerNetworkMode
|
|
|
|
// PreserveWorkingDir describes if working directory should be left after processing.
|
|
PreserveWorkingDir bool
|
|
|
|
// IgnoreSubmodules determines whether we will attempt to pull in submodules
|
|
// (via --recursive or submodule init)
|
|
IgnoreSubmodules bool
|
|
|
|
// Source URL describing the location of sources used to build the result image.
|
|
Source *git.URL
|
|
|
|
// Tag is a result image tag name.
|
|
Tag string
|
|
|
|
// BuilderPullPolicy specifies when to pull the builder image
|
|
BuilderPullPolicy PullPolicy
|
|
|
|
// PreviousImagePullPolicy specifies when to pull the previously build image
|
|
// when doing incremental build
|
|
PreviousImagePullPolicy PullPolicy
|
|
|
|
// Incremental describes whether to try to perform incremental build.
|
|
Incremental bool
|
|
|
|
// IncrementalFromTag sets an alternative image tag to look for existing
|
|
// artifacts. Tag is used by default if this is not set.
|
|
IncrementalFromTag string
|
|
|
|
// RemovePreviousImage describes if previous image should be removed after successful build.
|
|
// This applies only to incremental builds.
|
|
RemovePreviousImage bool
|
|
|
|
// Environment is a map of environment variables to be passed to the image.
|
|
Environment EnvironmentList
|
|
|
|
// EnvironmentFile provides the path to a file with list of environment
|
|
// variables.
|
|
EnvironmentFile string
|
|
|
|
// LabelNamespace provides the namespace under which the labels will be generated.
|
|
LabelNamespace string
|
|
|
|
// CallbackURL is a URL which is called upon successful build to inform about that fact.
|
|
CallbackURL string
|
|
|
|
// ScriptsURL is a URL describing where to fetch the S2I scripts from during build process.
|
|
// This url can be a reference within the builder image if the scheme is specified as image://
|
|
ScriptsURL string
|
|
|
|
// BuilderImageLabels is a map containing the builder image labels for possible adjustment of fields
|
|
// on this object.
|
|
BuilderImageLabels map[string]string
|
|
|
|
// Destination specifies a location where the untar operation will place its artifacts.
|
|
Destination string
|
|
|
|
// WorkingDir describes temporary directory used for downloading sources, scripts and tar operations.
|
|
WorkingDir string
|
|
|
|
// WorkingSourceDir describes the subdirectory off of WorkingDir set up during the repo download
|
|
// that is later used as the root for ignore processing
|
|
WorkingSourceDir string
|
|
|
|
// LayeredBuild describes if this is build which layered scripts and sources on top of BuilderImage.
|
|
LayeredBuild bool
|
|
|
|
// Operate quietly. Progress and assemble script output are not reported, only fatal errors.
|
|
// (default: false).
|
|
Quiet bool
|
|
|
|
// ForceCopy results in only the file SCM plugin being used (i.e. no `git clone`); allows for empty directories to be included
|
|
// in resulting image (since git does not support that).
|
|
// (default: false).
|
|
ForceCopy bool
|
|
|
|
// Specify a relative directory inside the application repository that should
|
|
// be used as a root directory for the application.
|
|
ContextDir string
|
|
|
|
// AllowedUIDs is a list of user ranges of users allowed to run the builder image.
|
|
// If a range is specified and the builder (or runtime) image uses a non-numeric
|
|
// user or a user that is outside the specified range, then the build fails.
|
|
AllowedUIDs user.RangeList
|
|
|
|
// AssembleUser specifies the user to run the assemble script in container
|
|
AssembleUser string
|
|
|
|
// RunImage will trigger a "docker run ..." invocation of the produced image so the user
|
|
// can see if it operates as he would expect
|
|
RunImage bool
|
|
|
|
// Usage allows for properly shortcircuiting s2i logic when `s2i usage` is invoked
|
|
Usage bool
|
|
|
|
// Injections specifies a list source/destination folders that are injected to
|
|
// the container that runs assemble.
|
|
// All files we inject will be truncated after the assemble script finishes.
|
|
Injections VolumeList
|
|
|
|
// CGroupLimits describes the cgroups limits that will be applied to any containers
|
|
// run by s2i.
|
|
CGroupLimits *CGroupLimits
|
|
|
|
// DropCapabilities contains a list of capabilities to drop when executing containers
|
|
DropCapabilities []string
|
|
|
|
// ScriptDownloadProxyConfig optionally specifies the http and https proxy
|
|
// to use when downloading scripts
|
|
ScriptDownloadProxyConfig *ProxyConfig
|
|
|
|
// ExcludeRegExp contains a string representation of the regular expression desired for
|
|
// deciding which files to exclude from the tar stream
|
|
ExcludeRegExp string
|
|
|
|
// BlockOnBuild prevents s2i from performing a docker build operation
|
|
// if one is necessary to execute ONBUILD commands, or to layer source code into
|
|
// the container for images that don't have a tar binary available, if the
|
|
// image contains ONBUILD commands that would be executed.
|
|
BlockOnBuild bool
|
|
|
|
// HasOnBuild will be set to true if the builder image contains ONBUILD instructions
|
|
HasOnBuild bool
|
|
|
|
// BuildVolumes specifies a list of volumes to mount to container running the
|
|
// build.
|
|
BuildVolumes []string
|
|
|
|
// Labels specify labels and their values to be applied to the resulting image. Label keys
|
|
// must have non-zero length. The labels defined here override generated labels in case
|
|
// they have the same name.
|
|
Labels map[string]string
|
|
|
|
// SourceInfo provides the info about the source to be built rather than relying
|
|
// on the Downloader to retrieve it.
|
|
SourceInfo *git.SourceInfo
|
|
|
|
// SecurityOpt are passed as options to the docker containers launched by s2i.
|
|
SecurityOpt []string
|
|
|
|
// KeepSymlinks indicates to copy symlinks as symlinks. Default behavior is to follow
|
|
// symlinks and copy files by content.
|
|
KeepSymlinks bool
|
|
|
|
// AsDockerfile indicates the path where the Dockerfile should be written instead of building
|
|
// a new image.
|
|
AsDockerfile string
|
|
|
|
// ImageWorkDir is the default working directory for the builder image.
|
|
ImageWorkDir string
|
|
|
|
// ImageScriptsURL is the default location to find the assemble/run scripts for a builder image.
|
|
// This url can be a reference within the builder image if the scheme is specified as image://
|
|
ImageScriptsURL string
|
|
// AddHost Add a line to /etc/hosts for test purpose or private use in LAN. Its format is host:IP,muliple hosts can be added by using multiple --add-host
|
|
AddHost []string
|
|
|
|
// AssembleRuntimeUser specifies the user to run the assemble-runtime script in container
|
|
AssembleRuntimeUser string
|
|
}
|
|
|
|
// EnvironmentSpec specifies a single environment variable.
|
|
type EnvironmentSpec struct {
|
|
Name string
|
|
Value string
|
|
}
|
|
|
|
// EnvironmentList contains list of environment variables.
|
|
type EnvironmentList []EnvironmentSpec
|
|
|
|
// ProxyConfig holds proxy configuration.
|
|
type ProxyConfig struct {
|
|
HTTPProxy *url.URL
|
|
HTTPSProxy *url.URL
|
|
}
|
|
|
|
// CGroupLimits holds limits used to constrain container resources.
|
|
type CGroupLimits struct {
|
|
MemoryLimitBytes int64
|
|
CPUShares int64
|
|
CPUPeriod int64
|
|
CPUQuota int64
|
|
MemorySwap int64
|
|
Parent string
|
|
}
|
|
|
|
// VolumeSpec represents a single volume mount point.
|
|
type VolumeSpec struct {
|
|
// Source is a reference to the volume source.
|
|
Source string
|
|
// Destination is the path to mount the volume to - absolute or relative.
|
|
Destination string
|
|
// Keep indicates if the mounted data should be kept in the final image.
|
|
Keep bool
|
|
}
|
|
|
|
// VolumeList contains list of VolumeSpec.
|
|
type VolumeList []VolumeSpec
|
|
|
|
// DockerConfig contains the configuration for a Docker connection.
|
|
type DockerConfig struct {
|
|
// Endpoint is the docker network endpoint or socket
|
|
Endpoint string
|
|
|
|
// CertFile is the certificate file path for a TLS connection
|
|
CertFile string
|
|
|
|
// KeyFile is the key file path for a TLS connection
|
|
KeyFile string
|
|
|
|
// CAFile is the certificate authority file path for a TLS connection
|
|
CAFile string
|
|
|
|
// UseTLS indicates if TLS must be used
|
|
UseTLS bool
|
|
|
|
// TLSVerify indicates if TLS peer must be verified
|
|
TLSVerify bool
|
|
}
|
|
|
|
// AuthConfig is our abstraction of the Registry authorization information for whatever
|
|
// docker client we happen to be based on
|
|
type AuthConfig struct {
|
|
Username string
|
|
Password string
|
|
Email string
|
|
ServerAddress string
|
|
}
|
|
|
|
// ContainerConfig is the abstraction of the docker client provider (formerly go-dockerclient, now either
|
|
// engine-api or kube docker client) container.Config type that is leveraged by s2i or origin
|
|
type ContainerConfig struct {
|
|
Labels map[string]string
|
|
Env []string
|
|
}
|
|
|
|
// Image is the abstraction of the docker client provider (formerly go-dockerclient, now either
|
|
// engine-api or kube docker client) Image type that is leveraged by s2i or origin
|
|
type Image struct {
|
|
ID string
|
|
*ContainerConfig
|
|
Config *ContainerConfig
|
|
}
|
|
|
|
// Result structure contains information from build process.
|
|
type Result struct {
|
|
|
|
// Success describes whether the build was successful.
|
|
Success bool
|
|
|
|
// Messages is a list of messages from build process.
|
|
Messages []string
|
|
|
|
// WorkingDir describes temporary directory used for downloading sources, scripts and tar operations.
|
|
WorkingDir string
|
|
|
|
// ImageID describes resulting image ID.
|
|
ImageID string
|
|
|
|
// BuildInfo holds information about the result of a build.
|
|
BuildInfo BuildInfo
|
|
}
|
|
|
|
// BuildInfo contains information about the build process.
|
|
type BuildInfo struct {
|
|
// Stages contains details about each build stage.
|
|
Stages []StageInfo
|
|
|
|
// FailureReason is a camel case reason that is used by the machine to reply
|
|
// back to the OpenShift builder with information why any of the steps in the
|
|
// build failed.
|
|
FailureReason FailureReason
|
|
}
|
|
|
|
// StageInfo contains details about a build stage.
|
|
type StageInfo struct {
|
|
// Name is the identifier for each build stage.
|
|
Name StageName
|
|
|
|
// StartTime identifies when this stage started.
|
|
StartTime time.Time
|
|
|
|
// DurationMilliseconds identifies how long this stage ran.
|
|
DurationMilliseconds int64
|
|
|
|
// Steps contains details about each build step within a build stage.
|
|
Steps []StepInfo
|
|
}
|
|
|
|
// StageName is the identifier for each build stage.
|
|
type StageName string
|
|
|
|
// Valid StageNames
|
|
const (
|
|
// StagePullImages pulls the docker images.
|
|
StagePullImages StageName = "PullImages"
|
|
|
|
//StageAssemble runs the assemble steps.
|
|
StageAssemble StageName = "Assemble"
|
|
|
|
// StageBuild builds the source.
|
|
StageBuild StageName = "Build"
|
|
|
|
// StageCommit commits the container.
|
|
StageCommit StageName = "CommitContainer"
|
|
|
|
// StageRetrieve retrieves artifacts.
|
|
StageRetrieve StageName = "RetrieveArtifacts"
|
|
)
|
|
|
|
// StepInfo contains details about a build step.
|
|
type StepInfo struct {
|
|
// Name is the identifier for each build step.
|
|
Name StepName
|
|
|
|
// StartTime identifies when this step started.
|
|
StartTime time.Time
|
|
|
|
// DurationMilliseconds identifies how long this step ran.
|
|
DurationMilliseconds int64
|
|
}
|
|
|
|
// StepName is the identifier for each build step.
|
|
type StepName string
|
|
|
|
// Valid StepNames
|
|
const (
|
|
// StepPullBuilderImage pulls the builder image.
|
|
StepPullBuilderImage StepName = "PullBuilderImage"
|
|
|
|
// StepPullPreviousImage pulls the previous image for an incremental build.
|
|
StepPullPreviousImage StepName = "PullPreviousImage"
|
|
|
|
// StepPullRuntimeImage pull the runtime image.
|
|
StepPullRuntimeImage StepName = "PullRuntimeImage"
|
|
|
|
// StepAssembleBuildScripts runs the assemble scripts.
|
|
StepAssembleBuildScripts StepName = "AssembleBuildScripts"
|
|
|
|
// StepBuildDockerImage builds the Docker image for layered builds.
|
|
StepBuildDockerImage StepName = "BuildDockerImage"
|
|
|
|
// StepCommitContainer commits the container to the builder image.
|
|
StepCommitContainer StepName = "CommitContainer"
|
|
|
|
// StepRetrievePreviousArtifacts restores archived artifacts from the previous build.
|
|
StepRetrievePreviousArtifacts StepName = "RetrievePreviousArtifacts"
|
|
)
|
|
|
|
// StepFailureReason holds the type of failure that occurred during the build
|
|
// process.
|
|
type StepFailureReason string
|
|
|
|
// StepFailureMessage holds the detailed message of a failure.
|
|
type StepFailureMessage string
|
|
|
|
// FailureReason holds the type of failure that occurred during the build
|
|
// process.
|
|
type FailureReason struct {
|
|
Reason StepFailureReason
|
|
Message StepFailureMessage
|
|
}
|
|
|
|
// InstallResult structure describes the result of install operation
|
|
type InstallResult struct {
|
|
|
|
// Script describes which script this result refers to
|
|
Script string
|
|
|
|
// URL describes from where the script was taken
|
|
URL string
|
|
|
|
// Downloaded describes if download operation happened, this will be true for
|
|
// external scripts, but false for scripts from inside the image
|
|
Downloaded bool
|
|
|
|
// Installed describes if script was installed to upload directory
|
|
Installed bool
|
|
|
|
// Error describes last error encountered during install operation
|
|
Error error
|
|
|
|
// FailedSources is a list of sources that were attempted but failed
|
|
// when downloading this script
|
|
FailedSources []string
|
|
}
|
|
|
|
// DockerNetworkMode specifies the network mode setting for the docker container
|
|
type DockerNetworkMode string
|
|
|
|
const (
|
|
// DockerNetworkModeHost places the container in the default (host) network namespace.
|
|
DockerNetworkModeHost DockerNetworkMode = "host"
|
|
// DockerNetworkModeBridge instructs docker to create a network namespace for this container connected to the docker0 bridge via a veth-pair.
|
|
DockerNetworkModeBridge DockerNetworkMode = "bridge"
|
|
// DockerNetworkModeContainerPrefix is the string prefix used by NewDockerNetworkModeContainer.
|
|
DockerNetworkModeContainerPrefix string = "container:"
|
|
// DockerNetworkModeNetworkNamespacePrefix is the string prefix used when sharing a namespace from a CRI-O container.
|
|
DockerNetworkModeNetworkNamespacePrefix string = "netns:"
|
|
)
|
|
|
|
// NewDockerNetworkModeContainer creates a DockerNetworkMode value which instructs docker to place the container in the network namespace of an existing container.
|
|
// It can be used, for instance, to place the s2i container in the network namespace of the infrastructure container of a k8s pod.
|
|
func NewDockerNetworkModeContainer(id string) DockerNetworkMode {
|
|
return DockerNetworkMode(DockerNetworkModeContainerPrefix + id)
|
|
}
|
|
|
|
// PullPolicy specifies a type for the method used to retrieve the Docker image
|
|
type PullPolicy string
|
|
|
|
// String implements the String() function of pflags.Value so this can be used as
|
|
// command line parameter.
|
|
// This method is really used just to show the default value when printing help.
|
|
// It will not default the configuration.
|
|
func (p *PullPolicy) String() string {
|
|
if len(string(*p)) == 0 {
|
|
return string(DefaultBuilderPullPolicy)
|
|
}
|
|
return string(*p)
|
|
}
|
|
|
|
// Type implements the Type() function of pflags.Value interface
|
|
func (p *PullPolicy) Type() string {
|
|
return "string"
|
|
}
|
|
|
|
// Set implements the Set() function of pflags.Value interface
|
|
// The valid options are "always", "never" or "if-not-present"
|
|
func (p *PullPolicy) Set(v string) error {
|
|
switch v {
|
|
case "always":
|
|
*p = PullAlways
|
|
case "never":
|
|
*p = PullNever
|
|
case "if-not-present":
|
|
*p = PullIfNotPresent
|
|
default:
|
|
return fmt.Errorf("invalid value %q, valid values are: always, never or if-not-present", v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsInvalidFilename verifies if the provided filename contains malicious
|
|
// characters.
|
|
func IsInvalidFilename(name string) bool {
|
|
return strings.ContainsAny(name, invalidFilenameCharacters)
|
|
}
|
|
|
|
// Set implements the Set() function of pflags.Value interface.
|
|
// This function parses the string that contains source:destination pair.
|
|
// When the destination is not specified, the source get copied into current
|
|
// working directory in container.
|
|
func (l *VolumeList) Set(value string) error {
|
|
volumes := strings.Split(value, ";")
|
|
newVols := make([]VolumeSpec, len(volumes))
|
|
for i, v := range volumes {
|
|
spec, err := l.parseSpec(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newVols[i] = *spec
|
|
}
|
|
*l = append(*l, newVols...)
|
|
return nil
|
|
}
|
|
|
|
func (l *VolumeList) parseSpec(value string) (*VolumeSpec, error) {
|
|
if len(value) == 0 {
|
|
return nil, errors.New("invalid format, must be source:destination")
|
|
}
|
|
var mount []string
|
|
pos := strings.LastIndex(value, ":")
|
|
if pos == -1 {
|
|
mount = []string{value, ""}
|
|
} else {
|
|
mount = []string{value[:pos], value[pos+1:]}
|
|
}
|
|
mount[0] = strings.Trim(mount[0], `"'`)
|
|
mount[1] = strings.Trim(mount[1], `"'`)
|
|
s := &VolumeSpec{Source: filepath.Clean(mount[0]), Destination: filepath.ToSlash(filepath.Clean(mount[1]))}
|
|
if IsInvalidFilename(s.Source) || IsInvalidFilename(s.Destination) {
|
|
return nil, fmt.Errorf("invalid characters in filename: %q", value)
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// String implements the String() function of pflags.Value interface.
|
|
func (l *VolumeList) String() string {
|
|
result := []string{}
|
|
for _, i := range *l {
|
|
result = append(result, strings.Join([]string{i.Source, i.Destination}, ":"))
|
|
}
|
|
return strings.Join(result, ",")
|
|
}
|
|
|
|
// Type implements the Type() function of pflags.Value interface.
|
|
func (l *VolumeList) Type() string {
|
|
return "string"
|
|
}
|
|
|
|
// Set implements the Set() function of pflags.Value interface.
|
|
func (e *EnvironmentList) Set(value string) error {
|
|
parts := strings.SplitN(value, "=", 2)
|
|
if len(parts) != 2 || len(parts[0]) == 0 {
|
|
return fmt.Errorf("invalid environment format %q, must be NAME=VALUE", value)
|
|
}
|
|
if strings.Contains(parts[1], ",") && strings.Contains(parts[1], "=") {
|
|
log.Warningf("DEPRECATED: Use multiple -e flags to specify multiple environment variables instead of comma (%q)", value)
|
|
}
|
|
*e = append(*e, EnvironmentSpec{
|
|
Name: strings.TrimSpace(parts[0]),
|
|
Value: strings.TrimSpace(parts[1]),
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// String implements the String() function of pflags.Value interface.
|
|
func (e *EnvironmentList) String() string {
|
|
result := []string{}
|
|
for _, i := range *e {
|
|
result = append(result, strings.Join([]string{i.Name, i.Value}, "="))
|
|
}
|
|
return strings.Join(result, ",")
|
|
}
|
|
|
|
// Type implements the Type() function of pflags.Value interface.
|
|
func (e *EnvironmentList) Type() string {
|
|
return "string"
|
|
}
|
|
|
|
// AsBinds converts the list of volume definitions to go-dockerclient compatible
|
|
// list of bind mounts.
|
|
func (l *VolumeList) AsBinds() []string {
|
|
result := make([]string, len(*l))
|
|
for index, v := range *l {
|
|
result[index] = strings.Join([]string{v.Source, v.Destination}, ":")
|
|
}
|
|
return result
|
|
}
|