diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index 4bcc504afea..81817aff8f8 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -16,6 +16,7 @@ go_library( "apply.go", "autoscale.go", "bash_comp_utils.go", + "cluster.go", "configmap.go", "custom_column_printer.go", "deployment.go", @@ -50,6 +51,7 @@ go_library( tags = ["automanaged"], deps = [ "//federation/apis/federation:go_default_library", + "//federation/apis/federation/v1beta1:go_default_library", "//federation/client/clientset_generated/federation_internalclientset:go_default_library", "//pkg/api:go_default_library", "//pkg/api/annotations:go_default_library", @@ -111,6 +113,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "cluster_test.go", "configmap_test.go", "custom_column_printer_test.go", "deployment_test.go", @@ -139,6 +142,7 @@ go_test( tags = ["automanaged"], deps = [ "//federation/apis/federation:go_default_library", + "//federation/apis/federation/v1beta1:go_default_library", "//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library", "//pkg/api:go_default_library", "//pkg/api/errors:go_default_library", diff --git a/pkg/kubectl/cluster.go b/pkg/kubectl/cluster.go new file mode 100644 index 00000000000..895817826b7 --- /dev/null +++ b/pkg/kubectl/cluster.go @@ -0,0 +1,125 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubectl + +import ( + "fmt" + + federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" +) + +// ClusterGeneratorV1Beta1 supports stable generation of a +// federation/cluster resource. +type ClusterGeneratorV1Beta1 struct { + // Name of the cluster context (required) + Name string + // ClientCIDR is the CIDR range in which the Kubernetes APIServer + // is available for the client (optional) + ClientCIDR string + // ServerAddress is the APIServer address of the Kubernetes cluster + // that is being registered (required) + ServerAddress string + // SecretName is the name of the secret that stores the credentials + // for the Kubernetes cluster that is being registered (optional) + SecretName string +} + +// Ensure it supports the generator pattern that uses parameter +// injection. +var _ Generator = &ClusterGeneratorV1Beta1{} + +// Ensure it supports the generator pattern that uses parameters +// specified during construction. +var _ StructuredGenerator = &ClusterGeneratorV1Beta1{} + +// Generate returns a cluster resource using the specified parameters. +func (s ClusterGeneratorV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) { + err := ValidateParams(s.ParamNames(), genericParams) + if err != nil { + return nil, err + } + clustergen := &ClusterGeneratorV1Beta1{} + params := map[string]string{} + for key, value := range genericParams { + strVal, isString := value.(string) + if !isString { + return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key) + } + params[key] = strVal + } + clustergen.Name = params["name"] + clustergen.ClientCIDR = params["client-cidr"] + clustergen.ServerAddress = params["server-address"] + clustergen.SecretName = params["secret"] + return clustergen.StructuredGenerate() +} + +// ParamNames returns the set of supported input parameters when using +// the parameter injection generator pattern. +func (s ClusterGeneratorV1Beta1) ParamNames() []GeneratorParam { + return []GeneratorParam{ + {"name", true}, + {"client-cidr", false}, + {"server-address", true}, + {"secret", false}, + } +} + +// StructuredGenerate outputs a federation cluster resource object +// using the configured fields. +func (s ClusterGeneratorV1Beta1) StructuredGenerate() (runtime.Object, error) { + if err := s.validate(); err != nil { + return nil, err + } + if s.ClientCIDR == "" { + s.ClientCIDR = "0.0.0.0/0" + } + if s.SecretName == "" { + s.SecretName = s.Name + } + cluster := &federationapi.Cluster{ + ObjectMeta: v1.ObjectMeta{ + Name: s.Name, + }, + Spec: federationapi.ClusterSpec{ + ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{ + { + ClientCIDR: s.ClientCIDR, + ServerAddress: s.ServerAddress, + }, + }, + SecretRef: &v1.LocalObjectReference{ + Name: s.SecretName, + }, + }, + } + return cluster, nil +} + +// validate validates required fields are set to support structured +// generation. +func (s ClusterGeneratorV1Beta1) validate() error { + if len(s.Name) == 0 { + return fmt.Errorf("name must be specified") + } + if len(s.ServerAddress) == 0 { + return fmt.Errorf("server address must be specified") + } + return nil +} diff --git a/pkg/kubectl/cluster_test.go b/pkg/kubectl/cluster_test.go new file mode 100644 index 00000000000..bea3c44964f --- /dev/null +++ b/pkg/kubectl/cluster_test.go @@ -0,0 +1,160 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubectl + +import ( + "reflect" + "testing" + + federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" + "k8s.io/kubernetes/pkg/api/v1" +) + +func TestClusterGenerate(t *testing.T) { + tests := []struct { + params map[string]interface{} + expected *federationapi.Cluster + expectErr bool + }{ + { + params: map[string]interface{}{ + "name": "foo", + "client-cidr": "0.0.0.0/0", + "server-address": "10.20.30.40", + "secret": "foo-credentials", + }, + expected: &federationapi.Cluster{ + ObjectMeta: v1.ObjectMeta{ + Name: "foo", + }, + Spec: federationapi.ClusterSpec{ + ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{ + { + ClientCIDR: "0.0.0.0/0", + ServerAddress: "10.20.30.40", + }, + }, + SecretRef: &v1.LocalObjectReference{ + Name: "foo-credentials", + }, + }, + }, + expectErr: false, + }, + { + params: map[string]interface{}{ + "name": "foo", + "client-cidr": "10.20.30.40/16", + "server-address": "https://foo.example.com", + "secret": "foo-credentials", + }, + expected: &federationapi.Cluster{ + ObjectMeta: v1.ObjectMeta{ + Name: "foo", + }, + Spec: federationapi.ClusterSpec{ + ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{ + { + ClientCIDR: "10.20.30.40/16", + ServerAddress: "https://foo.example.com", + }, + }, + SecretRef: &v1.LocalObjectReference{ + Name: "foo-credentials", + }, + }, + }, + expectErr: false, + }, + { + params: map[string]interface{}{ + "name": "bar-cluster", + "client-cidr": "10.20.30.40/16", + "server-address": "http://10.20.30.40", + "secret": "credentials", + }, + expected: &federationapi.Cluster{ + ObjectMeta: v1.ObjectMeta{ + Name: "bar-cluster", + }, + Spec: federationapi.ClusterSpec{ + ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{ + { + ClientCIDR: "10.20.30.40/16", + ServerAddress: "http://10.20.30.40", + }, + }, + SecretRef: &v1.LocalObjectReference{ + Name: "credentials", + }, + }, + }, + expectErr: false, + }, + { + params: map[string]interface{}{ + "server-address": "https://10.20.30.40", + }, + expected: nil, + expectErr: true, + }, + { + params: map[string]interface{}{ + "secret": "baz-credentials", + }, + expected: nil, + expectErr: true, + }, + { + params: map[string]interface{}{ + "server-address": "http://foo.example.com", + "secret": "foo-credentials", + }, + expected: nil, + expectErr: true, + }, + { + params: map[string]interface{}{ + "name": "foo", + "secret": "foo-credentials", + }, + expected: nil, + expectErr: true, + }, + { + params: map[string]interface{}{ + "name": "foo", + "client-cidr": "10.20.30.40/16", + }, + expected: nil, + expectErr: true, + }, + } + generator := ClusterGeneratorV1Beta1{} + for i, test := range tests { + obj, err := generator.Generate(test.params) + if !test.expectErr && err != nil { + t.Errorf("[%d] unexpected error: %v", i, err) + } + if test.expectErr && err != nil { + continue + } + if !reflect.DeepEqual(obj.(*federationapi.Cluster), test.expected) { + t.Errorf("\n[%d] want:\n%#v\n[%d] got:\n%#v", i, test.expected, i, obj.(*federationapi.Cluster)) + } + } +} diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 5edb0a36934..18181c7b8f0 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -205,6 +205,7 @@ const ( SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1" SecretForTLSV1GeneratorName = "secret-for-tls/v1" ConfigMapV1GeneratorName = "configmap/v1" + ClusterV1Beta1GeneratorName = "cluster/v1beta1" ) // DefaultGenerators returns the set of default generators for use in Factory instances