mirror of https://github.com/docker/cli.git
Handle version v1alpha3
Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
This commit is contained in:
parent
c863dbabf7
commit
2e5981d613
|
@ -103,7 +103,7 @@ func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) {
|
|||
}
|
||||
|
||||
func (c *KubeCli) composeClient() (*Factory, error) {
|
||||
return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet)
|
||||
return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet, c.ClientInfo().HasExperimental)
|
||||
}
|
||||
|
||||
func (c *KubeCli) checkHostsMatch() error {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
kubernetes "github.com/docker/compose-on-kubernetes/api"
|
||||
"github.com/docker/cli/kubernetes"
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubeclient "k8s.io/client-go/kubernetes"
|
||||
|
@ -18,10 +18,11 @@ type Factory struct {
|
|||
coreClientSet corev1.CoreV1Interface
|
||||
appsClientSet appsv1beta2.AppsV1beta2Interface
|
||||
clientSet *kubeclient.Clientset
|
||||
experimental bool
|
||||
}
|
||||
|
||||
// NewFactory creates a kubernetes client factory
|
||||
func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclient.Clientset) (*Factory, error) {
|
||||
func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclient.Clientset, experimental bool) (*Factory, error) {
|
||||
coreClientSet, err := corev1.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -38,6 +39,7 @@ func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclie
|
|||
coreClientSet: coreClientSet,
|
||||
appsClientSet: appsClientSet,
|
||||
clientSet: clientSet,
|
||||
experimental: experimental,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ func (s *Factory) DaemonSets() typesappsv1beta2.DaemonSetInterface {
|
|||
|
||||
// Stacks returns a client for Docker's Stack on Kubernetes
|
||||
func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) {
|
||||
version, err := kubernetes.GetStackAPIVersion(s.clientSet)
|
||||
version, err := kubernetes.GetStackAPIVersion(s.clientSet.Discovery(), s.experimental)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -97,7 +99,9 @@ func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) {
|
|||
return newStackV1Beta1(s.config, namespace)
|
||||
case kubernetes.StackAPIV1Beta2:
|
||||
return newStackV1Beta2(s.config, namespace)
|
||||
case kubernetes.StackAPIV1Alpha3:
|
||||
return newStackV1Alpha3(s.config, namespace)
|
||||
default:
|
||||
return nil, errors.Errorf("no supported Stack API version")
|
||||
return nil, errors.Errorf("unsupported stack API version: %q", version)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/docker/cli/cli/compose/schema"
|
||||
composeTypes "github.com/docker/cli/cli/compose/types"
|
||||
composetypes "github.com/docker/cli/cli/compose/types"
|
||||
latest "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"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -24,8 +25,8 @@ func NewStackConverter(version string) (StackConverter, error) {
|
|||
switch version {
|
||||
case "v1beta1":
|
||||
return stackV1Beta1Converter{}, nil
|
||||
case "v1beta2":
|
||||
return stackV1Beta2Converter{}, nil
|
||||
case "v1beta2", "v1alpha3":
|
||||
return stackV1Beta2OrHigherConverter{}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("stack version %s unsupported", version)
|
||||
}
|
||||
|
@ -61,9 +62,9 @@ func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *c
|
|||
return st, nil
|
||||
}
|
||||
|
||||
type stackV1Beta2Converter struct{}
|
||||
type stackV1Beta2OrHigherConverter struct{}
|
||||
|
||||
func (s stackV1Beta2Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
func (s stackV1Beta2OrHigherConverter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) {
|
||||
return fromCompose(stderr, name, cfg)
|
||||
}
|
||||
|
||||
|
@ -113,7 +114,38 @@ func stackToV1beta1(s Stack) *v1beta1.Stack {
|
|||
}
|
||||
}
|
||||
|
||||
func stackFromV1beta2(in *v1beta2.Stack) Stack {
|
||||
func stackFromV1beta2(in *v1beta2.Stack) (Stack, error) {
|
||||
var spec *latest.StackSpec
|
||||
if in.Spec != nil {
|
||||
spec = &latest.StackSpec{}
|
||||
if err := latest.Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec(in.Spec, spec, nil); err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
}
|
||||
return Stack{
|
||||
Name: in.ObjectMeta.Name,
|
||||
Namespace: in.ObjectMeta.Namespace,
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func stackToV1beta2(s Stack) (*v1beta2.Stack, error) {
|
||||
var spec *v1beta2.StackSpec
|
||||
if s.Spec != nil {
|
||||
spec = &v1beta2.StackSpec{}
|
||||
if err := latest.Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec(s.Spec, spec, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &v1beta2.Stack{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func stackFromV1alpha3(in *latest.Stack) Stack {
|
||||
return Stack{
|
||||
Name: in.ObjectMeta.Name,
|
||||
Namespace: in.ObjectMeta.Namespace,
|
||||
|
@ -121,8 +153,8 @@ func stackFromV1beta2(in *v1beta2.Stack) Stack {
|
|||
}
|
||||
}
|
||||
|
||||
func stackToV1beta2(s Stack) *v1beta2.Stack {
|
||||
return &v1beta2.Stack{
|
||||
func stackToV1alpha3(s Stack) *latest.Stack {
|
||||
return &latest.Stack{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
|
@ -130,32 +162,32 @@ func stackToV1beta2(s Stack) *v1beta2.Stack {
|
|||
}
|
||||
}
|
||||
|
||||
func fromComposeConfig(stderr io.Writer, c *composeTypes.Config) *v1beta2.StackSpec {
|
||||
func fromComposeConfig(stderr io.Writer, c *composeTypes.Config) *latest.StackSpec {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
warnUnsupportedFeatures(stderr, c)
|
||||
serviceConfigs := make([]v1beta2.ServiceConfig, len(c.Services))
|
||||
serviceConfigs := make([]latest.ServiceConfig, len(c.Services))
|
||||
for i, s := range c.Services {
|
||||
serviceConfigs[i] = fromComposeServiceConfig(s)
|
||||
}
|
||||
return &v1beta2.StackSpec{
|
||||
return &latest.StackSpec{
|
||||
Services: serviceConfigs,
|
||||
Secrets: fromComposeSecrets(c.Secrets),
|
||||
Configs: fromComposeConfigs(c.Configs),
|
||||
}
|
||||
}
|
||||
|
||||
func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]v1beta2.SecretConfig {
|
||||
func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]latest.SecretConfig {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
m := map[string]v1beta2.SecretConfig{}
|
||||
m := map[string]latest.SecretConfig{}
|
||||
for key, value := range s {
|
||||
m[key] = v1beta2.SecretConfig{
|
||||
m[key] = latest.SecretConfig{
|
||||
Name: value.Name,
|
||||
File: value.File,
|
||||
External: v1beta2.External{
|
||||
External: latest.External{
|
||||
Name: value.External.Name,
|
||||
External: value.External.External,
|
||||
},
|
||||
|
@ -165,16 +197,16 @@ func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]v1bet
|
|||
return m
|
||||
}
|
||||
|
||||
func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]v1beta2.ConfigObjConfig {
|
||||
func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]latest.ConfigObjConfig {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
m := map[string]v1beta2.ConfigObjConfig{}
|
||||
m := map[string]latest.ConfigObjConfig{}
|
||||
for key, value := range s {
|
||||
m[key] = v1beta2.ConfigObjConfig{
|
||||
m[key] = latest.ConfigObjConfig{
|
||||
Name: value.Name,
|
||||
File: value.File,
|
||||
External: v1beta2.External{
|
||||
External: latest.External{
|
||||
Name: value.External.Name,
|
||||
External: value.External.External,
|
||||
},
|
||||
|
@ -184,7 +216,7 @@ func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]v1
|
|||
return m
|
||||
}
|
||||
|
||||
func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfig {
|
||||
func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig {
|
||||
var userID *int64
|
||||
if s.User != "" {
|
||||
numerical, err := strconv.Atoi(s.User)
|
||||
|
@ -193,13 +225,13 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfi
|
|||
userID = &unixUserID
|
||||
}
|
||||
}
|
||||
return v1beta2.ServiceConfig{
|
||||
return latest.ServiceConfig{
|
||||
Name: s.Name,
|
||||
CapAdd: s.CapAdd,
|
||||
CapDrop: s.CapDrop,
|
||||
Command: s.Command,
|
||||
Configs: fromComposeServiceConfigs(s.Configs),
|
||||
Deploy: v1beta2.DeployConfig{
|
||||
Deploy: latest.DeployConfig{
|
||||
Mode: s.Deploy.Mode,
|
||||
Replicas: s.Deploy.Replicas,
|
||||
Labels: s.Deploy.Labels,
|
||||
|
@ -231,13 +263,13 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfi
|
|||
}
|
||||
}
|
||||
|
||||
func fromComposePorts(ports []composeTypes.ServicePortConfig) []v1beta2.ServicePortConfig {
|
||||
func fromComposePorts(ports []composeTypes.ServicePortConfig) []latest.ServicePortConfig {
|
||||
if ports == nil {
|
||||
return nil
|
||||
}
|
||||
p := make([]v1beta2.ServicePortConfig, len(ports))
|
||||
p := make([]latest.ServicePortConfig, len(ports))
|
||||
for i, port := range ports {
|
||||
p[i] = v1beta2.ServicePortConfig{
|
||||
p[i] = latest.ServicePortConfig{
|
||||
Mode: port.Mode,
|
||||
Target: port.Target,
|
||||
Published: port.Published,
|
||||
|
@ -247,13 +279,13 @@ func fromComposePorts(ports []composeTypes.ServicePortConfig) []v1beta2.ServiceP
|
|||
return p
|
||||
}
|
||||
|
||||
func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []v1beta2.ServiceSecretConfig {
|
||||
func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []latest.ServiceSecretConfig {
|
||||
if secrets == nil {
|
||||
return nil
|
||||
}
|
||||
c := make([]v1beta2.ServiceSecretConfig, len(secrets))
|
||||
c := make([]latest.ServiceSecretConfig, len(secrets))
|
||||
for i, secret := range secrets {
|
||||
c[i] = v1beta2.ServiceSecretConfig{
|
||||
c[i] = latest.ServiceSecretConfig{
|
||||
Source: secret.Source,
|
||||
Target: secret.Target,
|
||||
UID: secret.UID,
|
||||
|
@ -263,13 +295,13 @@ func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []v1b
|
|||
return c
|
||||
}
|
||||
|
||||
func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) []v1beta2.ServiceConfigObjConfig {
|
||||
func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) []latest.ServiceConfigObjConfig {
|
||||
if configs == nil {
|
||||
return nil
|
||||
}
|
||||
c := make([]v1beta2.ServiceConfigObjConfig, len(configs))
|
||||
c := make([]latest.ServiceConfigObjConfig, len(configs))
|
||||
for i, config := range configs {
|
||||
c[i] = v1beta2.ServiceConfigObjConfig{
|
||||
c[i] = latest.ServiceConfigObjConfig{
|
||||
Source: config.Source,
|
||||
Target: config.Target,
|
||||
UID: config.UID,
|
||||
|
@ -279,11 +311,11 @@ func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) []
|
|||
return c
|
||||
}
|
||||
|
||||
func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *v1beta2.HealthCheckConfig {
|
||||
func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *latest.HealthCheckConfig {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
return &v1beta2.HealthCheckConfig{
|
||||
return &latest.HealthCheckConfig{
|
||||
Test: h.Test,
|
||||
Timeout: composetypes.ConvertDurationPtr(h.Timeout),
|
||||
Interval: composetypes.ConvertDurationPtr(h.Interval),
|
||||
|
@ -291,8 +323,8 @@ func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *v1beta2.HealthCh
|
|||
}
|
||||
}
|
||||
|
||||
func fromComposePlacement(p composeTypes.Placement) v1beta2.Placement {
|
||||
return v1beta2.Placement{
|
||||
func fromComposePlacement(p composeTypes.Placement) latest.Placement {
|
||||
return latest.Placement{
|
||||
Constraints: fromComposeConstraints(p.Constraints),
|
||||
}
|
||||
}
|
||||
|
@ -306,18 +338,18 @@ const (
|
|||
swarmLabelPrefix = "node.labels."
|
||||
)
|
||||
|
||||
func fromComposeConstraints(s []string) *v1beta2.Constraints {
|
||||
func fromComposeConstraints(s []string) *latest.Constraints {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
constraints := &v1beta2.Constraints{}
|
||||
constraints := &latest.Constraints{}
|
||||
for _, constraint := range s {
|
||||
matches := constraintEquals.FindStringSubmatch(constraint)
|
||||
if len(matches) == 4 {
|
||||
key := matches[1]
|
||||
operator := matches[2]
|
||||
value := matches[3]
|
||||
constraint := &v1beta2.Constraint{
|
||||
constraint := &latest.Constraint{
|
||||
Operator: operator,
|
||||
Value: value,
|
||||
}
|
||||
|
@ -330,7 +362,7 @@ func fromComposeConstraints(s []string) *v1beta2.Constraints {
|
|||
constraints.Hostname = constraint
|
||||
case strings.HasPrefix(key, swarmLabelPrefix):
|
||||
if constraints.MatchLabels == nil {
|
||||
constraints.MatchLabels = map[string]v1beta2.Constraint{}
|
||||
constraints.MatchLabels = map[string]latest.Constraint{}
|
||||
}
|
||||
constraints.MatchLabels[strings.TrimPrefix(key, swarmLabelPrefix)] = *constraint
|
||||
}
|
||||
|
@ -339,48 +371,48 @@ func fromComposeConstraints(s []string) *v1beta2.Constraints {
|
|||
return constraints
|
||||
}
|
||||
|
||||
func fromComposeResources(r composeTypes.Resources) v1beta2.Resources {
|
||||
return v1beta2.Resources{
|
||||
func fromComposeResources(r composeTypes.Resources) latest.Resources {
|
||||
return latest.Resources{
|
||||
Limits: fromComposeResourcesResource(r.Limits),
|
||||
Reservations: fromComposeResourcesResource(r.Reservations),
|
||||
}
|
||||
}
|
||||
|
||||
func fromComposeResourcesResource(r *composeTypes.Resource) *v1beta2.Resource {
|
||||
func fromComposeResourcesResource(r *composeTypes.Resource) *latest.Resource {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return &v1beta2.Resource{
|
||||
return &latest.Resource{
|
||||
MemoryBytes: int64(r.MemoryBytes),
|
||||
NanoCPUs: r.NanoCPUs,
|
||||
}
|
||||
}
|
||||
|
||||
func fromComposeUpdateConfig(u *composeTypes.UpdateConfig) *v1beta2.UpdateConfig {
|
||||
func fromComposeUpdateConfig(u *composeTypes.UpdateConfig) *latest.UpdateConfig {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return &v1beta2.UpdateConfig{
|
||||
return &latest.UpdateConfig{
|
||||
Parallelism: u.Parallelism,
|
||||
}
|
||||
}
|
||||
|
||||
func fromComposeRestartPolicy(r *composeTypes.RestartPolicy) *v1beta2.RestartPolicy {
|
||||
func fromComposeRestartPolicy(r *composeTypes.RestartPolicy) *latest.RestartPolicy {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
return &v1beta2.RestartPolicy{
|
||||
return &latest.RestartPolicy{
|
||||
Condition: r.Condition,
|
||||
}
|
||||
}
|
||||
|
||||
func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []v1beta2.ServiceVolumeConfig {
|
||||
func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []latest.ServiceVolumeConfig {
|
||||
if vs == nil {
|
||||
return nil
|
||||
}
|
||||
volumes := []v1beta2.ServiceVolumeConfig{}
|
||||
volumes := []latest.ServiceVolumeConfig{}
|
||||
for _, v := range vs {
|
||||
volumes = append(volumes, v1beta2.ServiceVolumeConfig{
|
||||
volumes = append(volumes, latest.ServiceVolumeConfig{
|
||||
Type: v.Type,
|
||||
Source: v.Source,
|
||||
Target: v.Target,
|
||||
|
|
|
@ -15,4 +15,6 @@ func TestNewStackConverter(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
_, err = NewStackConverter("v1beta2")
|
||||
assert.NilError(t, err)
|
||||
_, err = NewStackConverter("v1alpha3")
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3"
|
||||
composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
|
||||
composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
|
||||
"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"
|
||||
"gotest.tools/assert"
|
||||
|
@ -31,11 +33,11 @@ configs:
|
|||
test:
|
||||
file: testdata/config
|
||||
`,
|
||||
Spec: &v1beta2.StackSpec{
|
||||
Configs: map[string]v1beta2.ConfigObjConfig{
|
||||
Spec: &v1alpha3.StackSpec{
|
||||
Configs: map[string]v1alpha3.ConfigObjConfig{
|
||||
"test": {Name: "test", File: "testdata/config"},
|
||||
},
|
||||
Secrets: map[string]v1beta2.SecretConfig{
|
||||
Secrets: map[string]v1alpha3.SecretConfig{
|
||||
"test": {Name: "test", File: "testdata/secret"},
|
||||
},
|
||||
},
|
||||
|
@ -86,6 +88,24 @@ func TestCreateChildResourcesV1Beta2(t *testing.T) {
|
|||
checkOwnerReferences(t, s.ObjectMeta, "test", v1beta2.SchemeGroupVersion.String())
|
||||
}
|
||||
|
||||
func TestCreateChildResourcesV1Alpha3(t *testing.T) {
|
||||
k8sclientSet := fake.NewSimpleClientset()
|
||||
stack := testStack()
|
||||
configs := k8sclientSet.CoreV1().ConfigMaps("test")
|
||||
secrets := k8sclientSet.CoreV1().Secrets("test")
|
||||
assert.NilError(t, createResources(
|
||||
stack,
|
||||
&stackV1Alpha3{stacks: &fakeV1alpha3Client{}},
|
||||
configs,
|
||||
secrets))
|
||||
c, err := configs.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String())
|
||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String())
|
||||
}
|
||||
|
||||
func TestCreateChildResourcesWithStackCreationErrorV1Beta1(t *testing.T) {
|
||||
k8sclientSet := fake.NewSimpleClientset()
|
||||
stack := testStack()
|
||||
|
@ -120,6 +140,23 @@ func TestCreateChildResourcesWithStackCreationErrorV1Beta2(t *testing.T) {
|
|||
assert.Check(t, kerrors.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestCreateChildResourcesWithStackCreationErrorV1Alpha3(t *testing.T) {
|
||||
k8sclientSet := fake.NewSimpleClientset()
|
||||
stack := testStack()
|
||||
configs := k8sclientSet.CoreV1().ConfigMaps("test")
|
||||
secrets := k8sclientSet.CoreV1().Secrets("test")
|
||||
err := createResources(
|
||||
stack,
|
||||
&stackV1Alpha3{stacks: &fakeV1alpha3Client{errorOnCreate: true}},
|
||||
configs,
|
||||
secrets)
|
||||
assert.Error(t, err, "some error")
|
||||
_, err = configs.Get("test", metav1.GetOptions{})
|
||||
assert.Check(t, kerrors.IsNotFound(err))
|
||||
_, err = secrets.Get("test", metav1.GetOptions{})
|
||||
assert.Check(t, kerrors.IsNotFound(err))
|
||||
}
|
||||
|
||||
type fakeV1beta1Client struct {
|
||||
errorOnCreate bool
|
||||
}
|
||||
|
@ -213,3 +250,50 @@ func (c *fakeV1beta2Client) Patch(name string, pt types.PatchType, data []byte,
|
|||
func (c *fakeV1beta2Client) WithSkipValidation() composev1beta2.StackInterface {
|
||||
return c
|
||||
}
|
||||
|
||||
type fakeV1alpha3Client struct {
|
||||
errorOnCreate bool
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) Create(s *v1alpha3.Stack) (*v1alpha3.Stack, error) {
|
||||
if c.errorOnCreate {
|
||||
return nil, errors.New("some error")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) Update(*v1alpha3.Stack) (*v1alpha3.Stack, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) UpdateStatus(*v1alpha3.Stack) (*v1alpha3.Stack, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) Delete(name string, options *metav1.DeleteOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) Get(name string, options metav1.GetOptions) (*v1alpha3.Stack, error) {
|
||||
return nil, kerrors.NewNotFound(v1beta1.SchemeGroupVersion.WithResource("stacks").GroupResource(), name)
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) List(opts metav1.ListOptions) (*v1alpha3.StackList, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) Watch(opts metav1.ListOptions) (watch.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha3.Stack, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeV1alpha3Client) WithSkipValidation() composev1alpha3.StackInterface {
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
|
||||
latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
|
||||
"github.com/docker/compose-on-kubernetes/api/labels"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -17,7 +17,7 @@ type Stack struct {
|
|||
Name string
|
||||
Namespace string
|
||||
ComposeFile string
|
||||
Spec *v1beta2.StackSpec
|
||||
Spec *latest.StackSpec
|
||||
}
|
||||
|
||||
type childResource interface {
|
||||
|
|
|
@ -3,8 +3,10 @@ package kubernetes
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3"
|
||||
composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
|
||||
composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
|
||||
"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"
|
||||
"github.com/docker/compose-on-kubernetes/api/labels"
|
||||
|
@ -123,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 {
|
||||
stackV1Beta2Converter
|
||||
stackV1Beta2OrHigherConverter
|
||||
stacks composev1beta2.StackInterface
|
||||
}
|
||||
|
||||
|
@ -136,17 +138,21 @@ func newStackV1Beta2(config *rest.Config, namespace string) (*stackV1Beta2, erro
|
|||
}
|
||||
|
||||
func (s *stackV1Beta2) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
|
||||
// If it already exists, update the stack
|
||||
var (
|
||||
stack *v1beta2.Stack
|
||||
err error
|
||||
)
|
||||
resolved, err := stackToV1beta2(internalStack)
|
||||
if err != nil {
|
||||
deleteChildResources(childResources)
|
||||
return err
|
||||
}
|
||||
if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
|
||||
stack.Spec = internalStack.Spec
|
||||
stack.Spec = resolved.Spec
|
||||
stack, err = s.stacks.Update(stack)
|
||||
} else {
|
||||
// Or create it
|
||||
stack, err = s.stacks.Create(stackToV1beta2(internalStack))
|
||||
stack, err = s.stacks.Create(resolved)
|
||||
}
|
||||
if err != nil {
|
||||
deleteChildResources(childResources)
|
||||
|
@ -173,7 +179,7 @@ func (s *stackV1Beta2) Get(name string) (Stack, error) {
|
|||
if err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
return stackFromV1beta2(stackBeta2), nil
|
||||
return stackFromV1beta2(stackBeta2)
|
||||
}
|
||||
|
||||
func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) {
|
||||
|
@ -183,7 +189,9 @@ func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) {
|
|||
}
|
||||
stacks := make([]Stack, len(list.Items))
|
||||
for i := range list.Items {
|
||||
stacks[i] = stackFromV1beta2(&list.Items[i])
|
||||
if stacks[i], err = stackFromV1beta2(&list.Items[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return stacks, nil
|
||||
}
|
||||
|
@ -192,3 +200,75 @@ func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) {
|
|||
func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2.
|
||||
type stackV1Alpha3 struct {
|
||||
stackV1Beta2OrHigherConverter
|
||||
stacks composev1alpha3.StackInterface
|
||||
}
|
||||
|
||||
func newStackV1Alpha3(config *rest.Config, namespace string) (*stackV1Alpha3, error) {
|
||||
client, err := composev1alpha3.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &stackV1Alpha3{stacks: client.Stacks(namespace)}, nil
|
||||
}
|
||||
|
||||
func (s *stackV1Alpha3) CreateOrUpdate(internalStack Stack, childResources []childResource) error {
|
||||
var (
|
||||
stack *v1alpha3.Stack
|
||||
err error
|
||||
)
|
||||
resolved := stackToV1alpha3(internalStack)
|
||||
if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil {
|
||||
stack.Spec = resolved.Spec
|
||||
stack, err = s.stacks.Update(stack)
|
||||
} else {
|
||||
// Or create it
|
||||
stack, err = s.stacks.Create(resolved)
|
||||
}
|
||||
if err != nil {
|
||||
deleteChildResources(childResources)
|
||||
return err
|
||||
}
|
||||
blockOwnerDeletion := true
|
||||
isController := true
|
||||
return setChildResourcesOwner(childResources, metav1.OwnerReference{
|
||||
APIVersion: v1alpha3.SchemeGroupVersion.String(),
|
||||
Kind: "Stack",
|
||||
Name: stack.Name,
|
||||
UID: stack.UID,
|
||||
BlockOwnerDeletion: &blockOwnerDeletion,
|
||||
Controller: &isController,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *stackV1Alpha3) Delete(name string) error {
|
||||
return s.stacks.Delete(name, &metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
func (s *stackV1Alpha3) Get(name string) (Stack, error) {
|
||||
stackAlpha3, err := s.stacks.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return Stack{}, err
|
||||
}
|
||||
return stackFromV1alpha3(stackAlpha3), nil
|
||||
}
|
||||
|
||||
func (s *stackV1Alpha3) List(opts metav1.ListOptions) ([]Stack, error) {
|
||||
list, err := s.stacks.List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stacks := make([]Stack, len(list.Items))
|
||||
for i := range list.Items {
|
||||
stacks[i] = stackFromV1alpha3(&list.Items[i])
|
||||
}
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// IsColliding is handle server side with the compose api v1beta2, so nothing to do here
|
||||
func (s *stackV1Alpha3) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
kubecontext "github.com/docker/cli/cli/context/kubernetes"
|
||||
"github.com/docker/cli/kubernetes"
|
||||
"github.com/docker/cli/templates"
|
||||
kubernetes "github.com/docker/compose-on-kubernetes/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -260,13 +260,13 @@ func getKubernetesVersion(dockerCli command.Cli, kubeConfig string) *kubernetesV
|
|||
logrus.Debugf("failed to get Kubernetes client: %s", err)
|
||||
return &version
|
||||
}
|
||||
version.StackAPI = getStackVersion(kubeClient)
|
||||
version.StackAPI = getStackVersion(kubeClient, dockerCli.ClientInfo().HasExperimental)
|
||||
version.Kubernetes = getKubernetesServerVersion(kubeClient)
|
||||
return &version
|
||||
}
|
||||
|
||||
func getStackVersion(client *kubernetesClient.Clientset) string {
|
||||
apiVersion, err := kubernetes.GetStackAPIVersion(client)
|
||||
func getStackVersion(client *kubernetesClient.Clientset, experimental bool) string {
|
||||
apiVersion, err := kubernetes.GetStackAPIVersion(client, experimental)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to get Stack API version: %s", err)
|
||||
return "Unknown"
|
||||
|
|
|
@ -1,20 +1,60 @@
|
|||
package kubernetes
|
||||
|
||||
import api "github.com/docker/compose-on-kubernetes/api"
|
||||
import (
|
||||
apiv1alpha3 "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
|
||||
apiv1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
|
||||
apiv1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
|
||||
"github.com/pkg/errors"
|
||||
apimachinerymetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
||||
// StackVersion represents the detected Compose Component on Kubernetes side.
|
||||
// Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackVersion instead
|
||||
type StackVersion = api.StackVersion
|
||||
type StackVersion string
|
||||
|
||||
const (
|
||||
// StackAPIV1Beta1 is returned if it's the most recent version available.
|
||||
// Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackAPIV1Beta1 instead
|
||||
StackAPIV1Beta1 = api.StackAPIV1Beta1
|
||||
StackAPIV1Beta1 = StackVersion("v1beta1")
|
||||
// StackAPIV1Beta2 is returned if it's the most recent version available.
|
||||
// Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackAPIV1Beta2 instead
|
||||
StackAPIV1Beta2 = api.StackAPIV1Beta2
|
||||
StackAPIV1Beta2 = StackVersion("v1beta2")
|
||||
// StackAPIV1Alpha3 is returned if it's the most recent version available, and experimental flag is on.
|
||||
StackAPIV1Alpha3 = StackVersion("v1alpha3")
|
||||
)
|
||||
|
||||
// GetStackAPIVersion returns the most recent stack API installed.
|
||||
// Deprecated: Use github.com/docker/compose-on-kubernetes/api.GetStackAPIVersion instead
|
||||
var GetStackAPIVersion = api.GetStackAPIVersion
|
||||
// GetStackAPIVersion returns the most appropriate stack API version installed.
|
||||
func GetStackAPIVersion(serverGroups discovery.ServerGroupsInterface, experimental bool) (StackVersion, error) {
|
||||
groups, err := serverGroups.ServerGroups()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return getAPIVersion(groups, experimental)
|
||||
}
|
||||
|
||||
func getAPIVersion(groups *metav1.APIGroupList, experimental bool) (StackVersion, error) {
|
||||
switch {
|
||||
case experimental && findVersion(apiv1alpha3.SchemeGroupVersion, groups.Groups):
|
||||
return StackAPIV1Alpha3, nil
|
||||
case findVersion(apiv1beta2.SchemeGroupVersion, groups.Groups):
|
||||
return StackAPIV1Beta2, nil
|
||||
case findVersion(apiv1beta1.SchemeGroupVersion, groups.Groups):
|
||||
return StackAPIV1Beta1, nil
|
||||
default:
|
||||
return "", errors.New("failed to find a Stack API version")
|
||||
}
|
||||
}
|
||||
|
||||
func findVersion(stackAPI schema.GroupVersion, groups []apimachinerymetav1.APIGroup) bool {
|
||||
for _, group := range groups {
|
||||
if group.Name == stackAPI.Group {
|
||||
for _, version := range group.Versions {
|
||||
if version.Version == stackAPI.Version {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestGetStackAPIVersion(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
groups *metav1.APIGroupList
|
||||
experimental bool
|
||||
err bool
|
||||
expectedStack StackVersion
|
||||
}{
|
||||
{"no stack api", makeGroups(), false, true, ""},
|
||||
{"v1beta1", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1"}}), false, false, StackAPIV1Beta1},
|
||||
{"v1beta2", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta2"}}), false, false, StackAPIV1Beta2},
|
||||
{"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2"}}), false, false, StackAPIV1Beta2},
|
||||
{"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2", "v1alpha3"}}), false, false, StackAPIV1Beta2},
|
||||
{"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2", "v1alpha3"}}), true, false, StackAPIV1Alpha3},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
version, err := getAPIVersion(test.groups, test.experimental)
|
||||
if test.err {
|
||||
assert.ErrorContains(t, err, "")
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
assert.Check(t, is.Equal(test.expectedStack, version))
|
||||
}
|
||||
}
|
||||
|
||||
type groupVersion struct {
|
||||
name string
|
||||
versions []string
|
||||
}
|
||||
|
||||
func makeGroups(versions ...groupVersion) *metav1.APIGroupList {
|
||||
groups := make([]metav1.APIGroup, len(versions))
|
||||
for i := range versions {
|
||||
groups[i].Name = versions[i].name
|
||||
for _, v := range versions[i].versions {
|
||||
groups[i].Versions = append(groups[i].Versions, metav1.GroupVersionForDiscovery{Version: v})
|
||||
}
|
||||
}
|
||||
return &metav1.APIGroupList{
|
||||
Groups: groups,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue