mirror of https://github.com/docker/cli.git
Merge pull request #1617 from simonferquel/pull-secrets
Add support for Kubernetes Pull secrets and Pull policies
This commit is contained in:
commit
5486cddbd9
|
@ -19,14 +19,23 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// pullSecretExtraField is an extra field on ServiceConfigs usable to reference a pull secret
|
||||
pullSecretExtraField = "x-pull-secret"
|
||||
// pullPolicyExtraField is an extra field on ServiceConfigs usable to specify a pull policy
|
||||
pullPolicyExtraField = "x-pull-policy"
|
||||
)
|
||||
|
||||
// NewStackConverter returns a converter from types.Config (compose) to the specified
|
||||
// stack version or error out if the version is not supported or existent.
|
||||
func NewStackConverter(version string) (StackConverter, error) {
|
||||
switch version {
|
||||
case "v1beta1":
|
||||
return stackV1Beta1Converter{}, nil
|
||||
case "v1beta2", "v1alpha3":
|
||||
return stackV1Beta2OrHigherConverter{}, nil
|
||||
case "v1beta2":
|
||||
return stackV1Beta2Converter{}, nil
|
||||
case "v1alpha3":
|
||||
return stackV1Alpha3Converter{}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("stack version %s unsupported", version)
|
||||
}
|
||||
|
@ -41,7 +50,7 @@ type stackV1Beta1Converter struct{}
|
|||
|
||||
func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
cfg.Version = v1beta1.MaxComposeVersion
|
||||
st, err := fromCompose(stderr, name, cfg)
|
||||
st, err := fromCompose(stderr, name, cfg, v1beta1Capabilities)
|
||||
if err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
|
@ -62,16 +71,26 @@ func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *c
|
|||
return st, nil
|
||||
}
|
||||
|
||||
type stackV1Beta2OrHigherConverter struct{}
|
||||
type stackV1Beta2Converter struct{}
|
||||
|
||||
func (s stackV1Beta2OrHigherConverter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
return fromCompose(stderr, name, cfg)
|
||||
func (s stackV1Beta2Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
return fromCompose(stderr, name, cfg, v1beta2Capabilities)
|
||||
}
|
||||
|
||||
func fromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
type stackV1Alpha3Converter struct{}
|
||||
|
||||
func (s stackV1Alpha3Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
return fromCompose(stderr, name, cfg, v1alpha3Capabilities)
|
||||
}
|
||||
|
||||
func fromCompose(stderr io.Writer, name string, cfg *composetypes.Config, capabilities composeCapabilities) (Stack, error) {
|
||||
spec, err := fromComposeConfig(stderr, cfg, capabilities)
|
||||
if err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
return Stack{
|
||||
Name: name,
|
||||
Spec: fromComposeConfig(stderr, cfg),
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -95,11 +114,15 @@ func stackFromV1beta1(in *v1beta1.Stack) (Stack, error) {
|
|||
if err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
spec, err := fromComposeConfig(ioutil.Discard, cfg, v1beta1Capabilities)
|
||||
if err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
return Stack{
|
||||
Name: in.ObjectMeta.Name,
|
||||
Namespace: in.ObjectMeta.Namespace,
|
||||
ComposeFile: in.Spec.ComposeFile,
|
||||
Spec: fromComposeConfig(ioutil.Discard, cfg),
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -162,20 +185,24 @@ func stackToV1alpha3(s Stack) *latest.Stack {
|
|||
}
|
||||
}
|
||||
|
||||
func fromComposeConfig(stderr io.Writer, c *composeTypes.Config) *latest.StackSpec {
|
||||
func fromComposeConfig(stderr io.Writer, c *composeTypes.Config, capabilities composeCapabilities) (*latest.StackSpec, error) {
|
||||
if c == nil {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
warnUnsupportedFeatures(stderr, c)
|
||||
serviceConfigs := make([]latest.ServiceConfig, len(c.Services))
|
||||
for i, s := range c.Services {
|
||||
serviceConfigs[i] = fromComposeServiceConfig(s)
|
||||
svc, err := fromComposeServiceConfig(s, capabilities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceConfigs[i] = svc
|
||||
}
|
||||
return &latest.StackSpec{
|
||||
Services: serviceConfigs,
|
||||
Secrets: fromComposeSecrets(c.Secrets),
|
||||
Configs: fromComposeConfigs(c.Configs),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]latest.SecretConfig {
|
||||
|
@ -216,8 +243,13 @@ func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]la
|
|||
return m
|
||||
}
|
||||
|
||||
func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig {
|
||||
var userID *int64
|
||||
func fromComposeServiceConfig(s composeTypes.ServiceConfig, capabilities composeCapabilities) (latest.ServiceConfig, error) {
|
||||
var (
|
||||
userID *int64
|
||||
pullSecret string
|
||||
pullPolicy string
|
||||
err error
|
||||
)
|
||||
if s.User != "" {
|
||||
numerical, err := strconv.Atoi(s.User)
|
||||
if err == nil {
|
||||
|
@ -225,6 +257,20 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig
|
|||
userID = &unixUserID
|
||||
}
|
||||
}
|
||||
pullSecret, err = resolveServiceExtra(s, pullSecretExtraField)
|
||||
if err != nil {
|
||||
return latest.ServiceConfig{}, err
|
||||
}
|
||||
pullPolicy, err = resolveServiceExtra(s, pullPolicyExtraField)
|
||||
if err != nil {
|
||||
return latest.ServiceConfig{}, err
|
||||
}
|
||||
if pullSecret != "" && !capabilities.hasPullSecrets {
|
||||
return latest.ServiceConfig{}, errors.Errorf("stack API version %s does not support pull secrets (field %q), please use version v1alpha3 or higher", capabilities.apiVersion, pullSecretExtraField)
|
||||
}
|
||||
if pullPolicy != "" && !capabilities.hasPullPolicies {
|
||||
return latest.ServiceConfig{}, errors.Errorf("stack API version %s does not support pull policies (field %q), please use version v1alpha3 or higher", capabilities.apiVersion, pullPolicyExtraField)
|
||||
}
|
||||
return latest.ServiceConfig{
|
||||
Name: s.Name,
|
||||
CapAdd: s.CapAdd,
|
||||
|
@ -260,7 +306,20 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig
|
|||
User: userID,
|
||||
Volumes: fromComposeServiceVolumeConfig(s.Volumes),
|
||||
WorkingDir: s.WorkingDir,
|
||||
PullSecret: pullSecret,
|
||||
PullPolicy: pullPolicy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveServiceExtra(s composeTypes.ServiceConfig, field string) (string, error) {
|
||||
if iface, ok := s.Extras[field]; ok {
|
||||
value, ok := iface.(string)
|
||||
if !ok {
|
||||
return "", errors.Errorf("field %q: value %v type is %T, should be a string", field, iface, iface)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func fromComposePorts(ports []composeTypes.ServicePortConfig) []latest.ServicePortConfig {
|
||||
|
@ -421,3 +480,23 @@ func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []lat
|
|||
}
|
||||
return volumes
|
||||
}
|
||||
|
||||
var (
|
||||
v1beta1Capabilities = composeCapabilities{
|
||||
apiVersion: "v1beta1",
|
||||
}
|
||||
v1beta2Capabilities = composeCapabilities{
|
||||
apiVersion: "v1beta2",
|
||||
}
|
||||
v1alpha3Capabilities = composeCapabilities{
|
||||
apiVersion: "v1alpha3",
|
||||
hasPullSecrets: true,
|
||||
hasPullPolicies: true,
|
||||
}
|
||||
)
|
||||
|
||||
type composeCapabilities struct {
|
||||
apiVersion string
|
||||
hasPullSecrets bool
|
||||
hasPullPolicies bool
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/compose/loader"
|
||||
composetypes "github.com/docker/cli/cli/compose/types"
|
||||
"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
|
||||
"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
|
||||
"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
|
||||
|
@ -161,3 +165,73 @@ func TestConvertFromToV1alpha3(t *testing.T) {
|
|||
gotBack := stackToV1alpha3(result)
|
||||
assert.DeepEqual(t, stackv1alpha3, gotBack)
|
||||
}
|
||||
|
||||
func loadTestStackWith(t *testing.T, with string) *composetypes.Config {
|
||||
t.Helper()
|
||||
filePath := fmt.Sprintf("testdata/compose-with-%s.yml", with)
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
assert.NilError(t, err)
|
||||
yamlData, err := loader.ParseYAML(data)
|
||||
assert.NilError(t, err)
|
||||
cfg, err := loader.Load(composetypes.ConfigDetails{
|
||||
ConfigFiles: []composetypes.ConfigFile{
|
||||
{Config: yamlData, Filename: filePath},
|
||||
},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
return cfg
|
||||
}
|
||||
|
||||
func TestHandlePullSecret(t *testing.T) {
|
||||
testData := loadTestStackWith(t, "pull-secret")
|
||||
cases := []struct {
|
||||
version string
|
||||
err string
|
||||
}{
|
||||
{version: "v1beta1", err: `stack API version v1beta1 does not support pull secrets (field "x-pull-secret"), please use version v1alpha3 or higher`},
|
||||
{version: "v1beta2", err: `stack API version v1beta2 does not support pull secrets (field "x-pull-secret"), please use version v1alpha3 or higher`},
|
||||
{version: "v1alpha3"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.version, func(t *testing.T) {
|
||||
conv, err := NewStackConverter(c.version)
|
||||
assert.NilError(t, err)
|
||||
s, err := conv.FromCompose(ioutil.Discard, "test", testData)
|
||||
if c.err != "" {
|
||||
assert.Error(t, err, c.err)
|
||||
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePullPolicy(t *testing.T) {
|
||||
testData := loadTestStackWith(t, "pull-policy")
|
||||
cases := []struct {
|
||||
version string
|
||||
err string
|
||||
}{
|
||||
{version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-pull-policy"), please use version v1alpha3 or higher`},
|
||||
{version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-pull-policy"), please use version v1alpha3 or higher`},
|
||||
{version: "v1alpha3"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.version, func(t *testing.T) {
|
||||
conv, err := NewStackConverter(c.version)
|
||||
assert.NilError(t, err)
|
||||
s, err := conv.FromCompose(ioutil.Discard, "test", testData)
|
||||
if c.err != "" {
|
||||
assert.Error(t, err, c.err)
|
||||
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ func verify(services corev1.ServiceInterface, stackName string, service string)
|
|||
|
||||
// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
|
||||
type stackV1Beta2 struct {
|
||||
stackV1Beta2OrHigherConverter
|
||||
stackV1Beta2Converter
|
||||
stacks composev1beta2.StackInterface
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st St
|
|||
|
||||
// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
|
||||
type stackV1Alpha3 struct {
|
||||
stackV1Beta2OrHigherConverter
|
||||
stackV1Alpha3Converter
|
||||
stacks composev1alpha3.StackInterface
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
version: "3.7"
|
||||
services:
|
||||
test:
|
||||
image: "some-image"
|
||||
x-pull-policy: "Never"
|
|
@ -0,0 +1,5 @@
|
|||
version: "3.7"
|
||||
services:
|
||||
test:
|
||||
image: "some-private-image"
|
||||
x-pull-secret: "some-secret"
|
|
@ -14,7 +14,7 @@ github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0
|
|||
github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af
|
||||
github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5
|
||||
github.com/docker/docker f76d6a078d881f410c00e8d900dcdfc2e026c841
|
||||
github.com/docker/compose-on-kubernetes 1559927c6b456d56cc9c9b05438252ebb646640b # master w/ v1alpha3
|
||||
github.com/docker/compose-on-kubernetes 356b2919c496f7e988f6e0dfe7e67d919602e14e # master w/ v1alpha3+pullsecrets+pull-policy
|
||||
github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962
|
||||
# the docker/go package contains a customized version of canonical/json
|
||||
# and is used by Notary. The package is periodically rebased on current Go versions.
|
||||
|
|
|
@ -97,6 +97,8 @@ type ServiceConfig struct {
|
|||
User *int64 `json:"user,omitempty"`
|
||||
Volumes []ServiceVolumeConfig `json:"volumes,omitempty"`
|
||||
WorkingDir string `json:"working_dir,omitempty"`
|
||||
PullSecret string `json:"pull_secret,omitempty"`
|
||||
PullPolicy string `json:"pull_policy,omitempty"`
|
||||
}
|
||||
|
||||
// ServicePortConfig is the port configuration for a service
|
||||
|
|
Loading…
Reference in New Issue