Merge pull request #42362 from soltysh/deployment_generators

Automatic merge from submit-queue (batch tested with PRs 38805, 42362, 42862)

Fix deployment generator after introducing deployments in apps/v1beta1

This PR does two things:

1. Switches all generator to produce versioned objects, to bypass the problem of having an object in multiple versions, which then results in not having stable generator (iow. producing exactly the same object).
2. Introduces new generator for `apps/v1beta1` deployments.

@kargakis @janetkuo ptal

@kubernetes/sig-apps-pr-reviews @kubernetes/sig-cli-pr-reviews ptal

This is a followup to https://github.com/kubernetes/kubernetes/pull/39683, so I'm adding 1.6 milestone.

```release-note
Introduce new generator for apps/v1beta1 deployments
```
This commit is contained in:
Kubernetes Submit Queue 2017-03-10 14:01:21 -08:00 committed by GitHub
commit d2d3884f83
13 changed files with 890 additions and 320 deletions

View File

@ -1046,11 +1046,23 @@ run_kubectl_run_tests() {
# Pre-Condition: no Deployment exists
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
kubectl run nginx "--image=$IMAGE_NGINX" --generator=deployment/v1beta1 "${kube_flags[@]}"
kubectl run nginx-extensions "--image=$IMAGE_NGINX" "${kube_flags[@]}"
# Post-Condition: Deployment "nginx" is created
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" 'nginx:'
kube::test::get_object_assert deployment.extensions "{{range.items}}{{$id_field}}:{{end}}" 'nginx-extensions:'
# and old generator was used, iow. old defaults are applied
output_message=$(kubectl get deployment.extensions/nginx-extensions -o jsonpath='{.spec.revisionHistoryLimit}')
kube::test::if_has_not_string "${output_message}" '2'
# Clean up
kubectl delete deployment nginx "${kube_flags[@]}"
kubectl delete deployment nginx-extensions "${kube_flags[@]}"
# Command
kubectl run nginx-apps "--image=$IMAGE_NGINX" --generator=deployment/apps.v1beta1 "${kube_flags[@]}"
# Post-Condition: Deployment "nginx" is created
kube::test::get_object_assert deployment.apps "{{range.items}}{{$id_field}}:{{end}}" 'nginx-apps:'
# and new generator was used, iow. new defaults are applied
output_message=$(kubectl get deployment/nginx-apps -o jsonpath='{.spec.revisionHistoryLimit}')
kube::test::if_has_string "${output_message}" '2'
# Clean up
kubectl delete deployment nginx-apps "${kube_flags[@]}"
}
run_kubectl_get_tests() {
@ -2283,17 +2295,35 @@ run_rc_tests() {
}
run_deployment_tests() {
# Test kubectl create deployment
kubectl create deployment test-nginx --image=gcr.io/google-containers/nginx:test-cmd
# Post-Condition: Deployment has 2 replicas defined in its spec.
kube::test::get_object_assert 'deploy test-nginx' "{{$container_name_field}}" 'nginx'
# Test kubectl create deployment (using default - old generator)
kubectl create deployment test-nginx-extensions --image=gcr.io/google-containers/nginx:test-cmd
# Post-Condition: Deployment "nginx" is created.
kube::test::get_object_assert 'deploy test-nginx-extensions' "{{$container_name_field}}" 'nginx'
# and old generator was used, iow. old defaults are applied
output_message=$(kubectl get deployment.extensions/test-nginx-extensions -o jsonpath='{.spec.revisionHistoryLimit}')
kube::test::if_has_not_string "${output_message}" '2'
# Ensure we can interact with deployments through extensions and apps endpoints
output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" 'extensions/v1beta1'
output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" 'apps/v1beta1'
# Clean up
kubectl delete deployment test-nginx "${kube_flags[@]}"
kubectl delete deployment test-nginx-extensions "${kube_flags[@]}"
# Test kubectl create deployment
kubectl create deployment test-nginx-apps --image=gcr.io/google-containers/nginx:test-cmd --generator=deployment-basic/apps.v1beta1
# Post-Condition: Deployment "nginx" is created.
kube::test::get_object_assert 'deploy test-nginx-apps' "{{$container_name_field}}" 'nginx'
# and new generator was used, iow. new defaults are applied
output_message=$(kubectl get deployment/test-nginx-apps -o jsonpath='{.spec.revisionHistoryLimit}')
kube::test::if_has_string "${output_message}" '2'
# Ensure we can interact with deployments through extensions and apps endpoints
output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" 'extensions/v1beta1'
output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" 'apps/v1beta1'
# Clean up
kubectl delete deployment test-nginx-apps "${kube_flags[@]}"
### Test cascading deletion
## Test that rs is deleted when deployment is deleted.

View File

@ -53,8 +53,11 @@ go_library(
"//pkg/api/util:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/v1:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",
@ -135,8 +138,12 @@ go_test(
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/v1:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",

View File

@ -70,7 +70,9 @@ go_library(
"//pkg/api/annotations:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/apis/batch/v1:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",

View File

@ -94,7 +94,7 @@ func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdCreateConfigMap(f, out))
cmd.AddCommand(NewCmdCreateServiceAccount(f, out))
cmd.AddCommand(NewCmdCreateService(f, out, errOut))
cmd.AddCommand(NewCmdCreateDeployment(f, out))
cmd.AddCommand(NewCmdCreateDeployment(f, out, errOut))
cmd.AddCommand(NewCmdCreateClusterRole(f, out))
cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, out))
cmd.AddCommand(NewCmdCreateRole(f, out))

View File

@ -22,6 +22,7 @@ import (
"github.com/spf13/cobra"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -38,7 +39,7 @@ var (
)
// NewCmdCreateDeployment is a macro command to create a new deployment
func NewCmdCreateDeployment(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
func NewCmdCreateDeployment(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "deployment NAME --image=image [--dry-run]",
Aliases: []string{"deploy"},
@ -46,7 +47,7 @@ func NewCmdCreateDeployment(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command
Long: deploymentLong,
Example: deploymentExample,
Run: func(cmd *cobra.Command, args []string) {
err := CreateDeployment(f, cmdOut, cmd, args)
err := CreateDeployment(f, cmdOut, cmdErr, cmd, args)
cmdutil.CheckErr(err)
},
}
@ -60,13 +61,33 @@ func NewCmdCreateDeployment(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command
}
// CreateDeployment implements the behavior to run the create deployment command
func CreateDeployment(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
func CreateDeployment(f cmdutil.Factory, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
clientset, err := f.ClientSet()
if err != nil {
return err
}
resourcesList, err := clientset.Discovery().ServerResources()
// ServerResources ignores errors for old servers do not expose discovery
if err != nil {
return fmt.Errorf("failed to discover supported resources: %v", err)
}
generatorName := cmdutil.GetFlagString(cmd, "generator")
// fallback to the old generator if server does not support apps/v1beta1 deployments
if generatorName == cmdutil.DeploymentBasicAppsV1Beta1GeneratorName &&
!contains(resourcesList, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) {
fmt.Fprintf(cmdErr, "WARNING: New deployments generator specified (%s), but apps/v1beta1.Deployments are not available, falling back to the old one (%s).\n",
cmdutil.DeploymentBasicAppsV1Beta1GeneratorName, cmdutil.DeploymentBasicV1Beta1GeneratorName)
generatorName = cmdutil.DeploymentBasicV1Beta1GeneratorName
}
var generator kubectl.StructuredGenerator
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
switch generatorName {
case cmdutil.DeploymentBasicAppsV1Beta1GeneratorName:
generator = &kubectl.DeploymentBasicAppsGeneratorV1{Name: name, Images: cmdutil.GetFlagStringSlice(cmd, "image")}
case cmdutil.DeploymentBasicV1Beta1GeneratorName:
generator = &kubectl.DeploymentBasicGeneratorV1{Name: name, Images: cmdutil.GetFlagStringSlice(cmd, "image")}
default:

View File

@ -18,20 +18,37 @@ package cmd
import (
"bytes"
"io/ioutil"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
)
func TestCreateDeployment(t *testing.T) {
depName := "jonny-dep"
f, tf, _, _ := cmdtesting.NewAPIFactory()
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(&bytes.Buffer{}),
}, nil
}),
}
tf.ClientConfig = &restclient.Config{}
tf.Printer = &testPrinter{}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateDeployment(f, buf)
cmd := NewCmdCreateDeployment(f, buf, buf)
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "name")
cmd.Flags().Set("image", "hollywood/jonny.depp:v2")
@ -44,13 +61,25 @@ func TestCreateDeployment(t *testing.T) {
func TestCreateDeploymentNoImage(t *testing.T) {
depName := "jonny-dep"
f, tf, _, _ := cmdtesting.NewAPIFactory()
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(&bytes.Buffer{}),
}, nil
}),
}
tf.ClientConfig = &restclient.Config{}
tf.Printer = &testPrinter{}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdCreateDeployment(f, buf)
cmd := NewCmdCreateDeployment(f, buf, buf)
cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("output", "name")
err := CreateDeployment(f, buf, cmd, []string{depName})
err := CreateDeployment(f, buf, buf, cmd, []string{depName})
assert.Error(t, err, "at least one image must be specified")
}

View File

@ -33,8 +33,10 @@ import (
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/kubernetes/pkg/api"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
conditions "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
@ -196,38 +198,55 @@ func Run(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobr
return err
}
clientset, err := f.ClientSet()
if err != nil {
return err
}
resourcesList, err := clientset.Discovery().ServerResources()
// ServerResources ignores errors for old servers do not expose discovery
if err != nil {
return fmt.Errorf("failed to discover supported resources: %v", err)
}
generatorName := cmdutil.GetFlagString(cmd, "generator")
schedule := cmdutil.GetFlagString(cmd, "schedule")
if len(schedule) != 0 && len(generatorName) == 0 {
generatorName = "cronjob/v2alpha1"
generatorName = cmdutil.CronJobV2Alpha1GeneratorName
}
if len(generatorName) == 0 {
clientset, err := f.ClientSet()
if err != nil {
return err
}
resourcesList, err := clientset.Discovery().ServerResources()
// ServerResources ignores errors for old servers do not expose discovery
if err != nil {
return fmt.Errorf("failed to discover supported resources: %v", err)
}
switch restartPolicy {
case api.RestartPolicyAlways:
if contains(resourcesList, v1beta1.SchemeGroupVersion.WithResource("deployments")) {
generatorName = "deployment/v1beta1"
// TODO: we need to deprecate this along with extensions/v1beta1.Deployments
// in favor of the new generator for apps/v1beta1.Deployments
if contains(resourcesList, extensionsv1beta1.SchemeGroupVersion.WithResource("deployments")) {
generatorName = cmdutil.DeploymentV1Beta1GeneratorName
} else {
generatorName = "run/v1"
generatorName = cmdutil.RunV1GeneratorName
}
case api.RestartPolicyOnFailure:
if contains(resourcesList, batchv1.SchemeGroupVersion.WithResource("jobs")) {
generatorName = "job/v1"
generatorName = cmdutil.JobV1GeneratorName
} else {
generatorName = "run-pod/v1"
generatorName = cmdutil.RunPodV1GeneratorName
}
case api.RestartPolicyNever:
generatorName = "run-pod/v1"
generatorName = cmdutil.RunPodV1GeneratorName
}
}
// TODO: this should be removed alongside with extensions/v1beta1 depployments generator
if generatorName == cmdutil.DeploymentAppsV1Beta1GeneratorName &&
!contains(resourcesList, appsv1beta1.SchemeGroupVersion.WithResource("deployments")) {
fmt.Fprintf(cmdErr, "WARNING: New deployments generator specified (%s), but apps/v1beta1.Deployments are not available, falling back to the old one (%s).\n",
cmdutil.DeploymentAppsV1Beta1GeneratorName, cmdutil.DeploymentV1Beta1GeneratorName)
generatorName = cmdutil.DeploymentV1Beta1GeneratorName
}
if generatorName == cmdutil.CronJobV2Alpha1GeneratorName &&
!contains(resourcesList, batchv2alpha1.SchemeGroupVersion.WithResource("cronjobs")) {
return fmt.Errorf("CronJob generator specified, but batch/v2alpha1.CronJobs are not available")
}
generators := f.Generators("run")
generator, found := generators[generatorName]
if !found {

View File

@ -35,6 +35,7 @@ import (
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@ -113,7 +114,13 @@ func TestGetEnv(t *testing.T) {
}
func TestRunArgsFollowDashRules(t *testing.T) {
_, _, rc := testData()
one := int32(1)
rc := &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"},
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
},
}
tests := []struct {
args []string
@ -158,7 +165,14 @@ func TestRunArgsFollowDashRules(t *testing.T) {
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, &rc.Items[0])}, nil
if req.URL.Path == "/namespaces/test/replicationcontrollers" {
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, rc)}, nil
} else {
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(&bytes.Buffer{}),
}, nil
}
}),
}
tf.Namespace = "test"

View File

@ -456,31 +456,33 @@ func (f *ring0Factory) DefaultNamespace() (string, bool, error) {
}
const (
RunV1GeneratorName = "run/v1"
RunPodV1GeneratorName = "run-pod/v1"
ServiceV1GeneratorName = "service/v1"
ServiceV2GeneratorName = "service/v2"
ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
ServiceExternalNameGeneratorV1Name = "service-externalname/v1"
ServiceAccountV1GeneratorName = "serviceaccount/v1"
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1"
JobV1GeneratorName = "job/v1"
CronJobV2Alpha1GeneratorName = "cronjob/v2alpha1"
ScheduledJobV2Alpha1GeneratorName = "scheduledjob/v2alpha1"
NamespaceV1GeneratorName = "namespace/v1"
ResourceQuotaV1GeneratorName = "resourcequotas/v1"
SecretV1GeneratorName = "secret/v1"
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
ConfigMapV1GeneratorName = "configmap/v1"
ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1"
RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1"
ClusterV1Beta1GeneratorName = "cluster/v1beta1"
PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1"
RunV1GeneratorName = "run/v1"
RunPodV1GeneratorName = "run-pod/v1"
ServiceV1GeneratorName = "service/v1"
ServiceV2GeneratorName = "service/v2"
ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
ServiceExternalNameGeneratorV1Name = "service-externalname/v1"
ServiceAccountV1GeneratorName = "serviceaccount/v1"
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
DeploymentAppsV1Beta1GeneratorName = "deployment/apps.v1beta1"
DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1"
DeploymentBasicAppsV1Beta1GeneratorName = "deployment-basic/apps.v1beta1"
JobV1GeneratorName = "job/v1"
CronJobV2Alpha1GeneratorName = "cronjob/v2alpha1"
ScheduledJobV2Alpha1GeneratorName = "scheduledjob/v2alpha1"
NamespaceV1GeneratorName = "namespace/v1"
ResourceQuotaV1GeneratorName = "resourcequotas/v1"
SecretV1GeneratorName = "secret/v1"
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
ConfigMapV1GeneratorName = "configmap/v1"
ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1"
RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1"
ClusterV1Beta1GeneratorName = "cluster/v1beta1"
PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1"
)
// DefaultGenerators returns the set of default generators for use in Factory instances
@ -506,16 +508,18 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
}
case "deployment":
generator = map[string]kubectl.Generator{
DeploymentBasicV1Beta1GeneratorName: kubectl.DeploymentBasicGeneratorV1{},
DeploymentBasicV1Beta1GeneratorName: kubectl.DeploymentBasicGeneratorV1{},
DeploymentBasicAppsV1Beta1GeneratorName: kubectl.DeploymentBasicAppsGeneratorV1{},
}
case "run":
generator = map[string]kubectl.Generator{
RunV1GeneratorName: kubectl.BasicReplicationController{},
RunPodV1GeneratorName: kubectl.BasicPod{},
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
JobV1GeneratorName: kubectl.JobV1{},
ScheduledJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
CronJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
RunV1GeneratorName: kubectl.BasicReplicationController{},
RunPodV1GeneratorName: kubectl.BasicPod{},
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
DeploymentAppsV1Beta1GeneratorName: kubectl.DeploymentAppsV1Beta1{},
JobV1GeneratorName: kubectl.JobV1{},
ScheduledJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
CronJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
}
case "autoscale":
generator = map[string]kubectl.Generator{

View File

@ -22,8 +22,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/api/v1"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)
// DeploymentBasicGeneratorV1 supports stable generation of a deployment
@ -65,7 +66,7 @@ func (s *DeploymentBasicGeneratorV1) StructuredGenerate() (runtime.Object, error
return nil, err
}
podSpec := api.PodSpec{Containers: []api.Container{}}
podSpec := v1.PodSpec{Containers: []v1.Container{}}
for _, imageString := range s.Images {
// Retain just the image name
imageSplit := strings.Split(imageString, "/")
@ -76,22 +77,23 @@ func (s *DeploymentBasicGeneratorV1) StructuredGenerate() (runtime.Object, error
} else if strings.Contains(name, "@") {
name = strings.Split(name, "@")[0]
}
podSpec.Containers = append(podSpec.Containers, api.Container{Name: name, Image: imageString})
podSpec.Containers = append(podSpec.Containers, v1.Container{Name: name, Image: imageString})
}
// setup default label and selector
labels := map[string]string{}
labels["app"] = s.Name
one := int32(1)
selector := metav1.LabelSelector{MatchLabels: labels}
deployment := extensions.Deployment{
deployment := extensionsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: s.Name,
Labels: labels,
},
Spec: extensions.DeploymentSpec{
Replicas: 1,
Spec: extensionsv1beta1.DeploymentSpec{
Replicas: &one,
Selector: &selector,
Template: api.PodTemplateSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
@ -112,3 +114,91 @@ func (s *DeploymentBasicGeneratorV1) validate() error {
}
return nil
}
// DeploymentBasicAppsGeneratorV1 supports stable generation of a deployment under apps/v1beta1 endpoint
type DeploymentBasicAppsGeneratorV1 struct {
Name string
Images []string
}
// Ensure it supports the generator pattern that uses parameters specified during construction
var _ StructuredGenerator = &DeploymentBasicAppsGeneratorV1{}
func (DeploymentBasicAppsGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"image", true},
}
}
func (s DeploymentBasicAppsGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), params)
if err != nil {
return nil, err
}
name, isString := params["name"].(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for 'name'", name)
}
imageStrings, isArray := params["image"].([]string)
if !isArray {
return nil, fmt.Errorf("expected []string, found :%v", imageStrings)
}
delegate := &DeploymentBasicAppsGeneratorV1{Name: name, Images: imageStrings}
return delegate.StructuredGenerate()
}
// StructuredGenerate outputs a deployment object using the configured fields
func (s *DeploymentBasicAppsGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err := s.validate(); err != nil {
return nil, err
}
podSpec := v1.PodSpec{Containers: []v1.Container{}}
for _, imageString := range s.Images {
// Retain just the image name
imageSplit := strings.Split(imageString, "/")
name := imageSplit[len(imageSplit)-1]
// Remove any tag or hash
if strings.Contains(name, ":") {
name = strings.Split(name, ":")[0]
} else if strings.Contains(name, "@") {
name = strings.Split(name, "@")[0]
}
podSpec.Containers = append(podSpec.Containers, v1.Container{Name: name, Image: imageString})
}
// setup default label and selector
labels := map[string]string{}
labels["app"] = s.Name
one := int32(1)
selector := metav1.LabelSelector{MatchLabels: labels}
deployment := appsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: s.Name,
Labels: labels,
},
Spec: appsv1beta1.DeploymentSpec{
Replicas: &one,
Selector: &selector,
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: podSpec,
},
},
}
return &deployment, nil
}
// validate validates required fields are set to support structured generation
func (s *DeploymentBasicAppsGeneratorV1) validate() error {
if len(s.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(s.Images) == 0 {
return fmt.Errorf("at least one image must be specified")
}
return nil
}

View File

@ -21,14 +21,16 @@ import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/api/v1"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)
func TestDeploymentGenerate(t *testing.T) {
one := int32(1)
tests := []struct {
params map[string]interface{}
expected *extensions.Deployment
expected *extensionsv1beta1.Deployment
expectErr bool
}{
{
@ -36,20 +38,20 @@ func TestDeploymentGenerate(t *testing.T) {
"name": "foo",
"image": []string{"abc/app:v4"},
},
expected: &extensions.Deployment{
expected: &extensionsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"app": "foo"},
},
Spec: extensions.DeploymentSpec{
Replicas: 1,
Spec: extensionsv1beta1.DeploymentSpec{
Replicas: &one,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}},
Template: api.PodTemplateSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "app", Image: "abc/app:v4"}},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "app", Image: "abc/app:v4"}},
},
},
},
@ -61,20 +63,20 @@ func TestDeploymentGenerate(t *testing.T) {
"name": "foo",
"image": []string{"abc/app:v4", "zyx/ape"},
},
expected: &extensions.Deployment{
expected: &extensionsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"app": "foo"},
},
Spec: extensions.DeploymentSpec{
Replicas: 1,
Spec: extensionsv1beta1.DeploymentSpec{
Replicas: &one,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}},
Template: api.PodTemplateSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "app", Image: "abc/app:v4"},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "app", Image: "abc/app:v4"},
{Name: "ape", Image: "zyx/ape"}},
},
},
@ -128,8 +130,118 @@ func TestDeploymentGenerate(t *testing.T) {
case !test.expectErr && err == nil:
// do nothing and drop through
}
if !reflect.DeepEqual(obj.(*extensions.Deployment), test.expected) {
t.Errorf("expected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensions.Deployment))
if !reflect.DeepEqual(obj.(*extensionsv1beta1.Deployment), test.expected) {
t.Errorf("expected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensionsv1beta1.Deployment))
}
}
}
func TestAppsDeploymentGenerate(t *testing.T) {
one := int32(1)
tests := []struct {
params map[string]interface{}
expected *appsv1beta1.Deployment
expectErr bool
}{
{
params: map[string]interface{}{
"name": "foo",
"image": []string{"abc/app:v4"},
},
expected: &appsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"app": "foo"},
},
Spec: appsv1beta1.DeploymentSpec{
Replicas: &one,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "foo"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "app", Image: "abc/app:v4"}},
},
},
},
},
expectErr: false,
},
{
params: map[string]interface{}{
"name": "foo",
"image": []string{"abc/app:v4", "zyx/ape"},
},
expected: &appsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"app": "foo"},
},
Spec: appsv1beta1.DeploymentSpec{
Replicas: &one,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app": "foo"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "app", Image: "abc/app:v4"},
{Name: "ape", Image: "zyx/ape"}},
},
},
},
},
expectErr: false,
},
{
params: map[string]interface{}{},
expectErr: true,
},
{
params: map[string]interface{}{
"name": 1,
},
expectErr: true,
},
{
params: map[string]interface{}{
"name": nil,
},
expectErr: true,
},
{
params: map[string]interface{}{
"name": "foo",
"image": []string{},
},
expectErr: true,
},
{
params: map[string]interface{}{
"NAME": "some_value",
},
expectErr: true,
},
}
generator := DeploymentBasicAppsGeneratorV1{}
for index, test := range tests {
t.Logf("running scenario %d", index)
obj, err := generator.Generate(test.params)
switch {
case test.expectErr && err != nil:
continue // loop, since there's no output to check
case test.expectErr && err == nil:
t.Errorf("expected error and didn't get one")
continue // loop, no expected output object
case !test.expectErr && err != nil:
t.Errorf("unexpected error %v", err)
continue // loop, no output object
case !test.expectErr && err == nil:
// do nothing and drop through
}
if !reflect.DeepEqual(obj.(*appsv1beta1.Deployment), test.expected) {
t.Errorf("expected:\n%#v\nsaw:\n%#v", test.expected, obj.(*appsv1beta1.Deployment))
}
}
}

View File

@ -26,8 +26,11 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/api/v1"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)
type DeploymentV1Beta1 struct{}
@ -88,7 +91,7 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime
return nil, err
}
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -99,15 +102,16 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime
// TODO: use versioned types for generators so that we don't need to
// set default values manually (see issue #17384)
deployment := extensions.Deployment{
count32 := int32(count)
deployment := extensionsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: extensions.DeploymentSpec{
Replicas: int32(count),
Spec: extensionsv1beta1.DeploymentSpec{
Replicas: &count32,
Selector: &metav1.LabelSelector{MatchLabels: labels},
Template: api.PodTemplateSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
@ -118,6 +122,94 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime
return &deployment, nil
}
type DeploymentAppsV1Beta1 struct{}
func (DeploymentAppsV1Beta1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"labels", false},
{"default-name", false},
{"name", true},
{"replicas", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
{"tty", false},
{"command", false},
{"args", false},
{"env", false},
{"requests", false},
{"limits", false},
}
}
func (DeploymentAppsV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
args, err := getArgs(genericParams)
if err != nil {
return nil, err
}
envs, err := getEnvs(genericParams)
if err != nil {
return nil, err
}
params, err := getParams(genericParams)
if err != nil {
return nil, err
}
name, err := getName(params)
if err != nil {
return nil, err
}
labels, err := getLabels(params, true, name)
if err != nil {
return nil, err
}
count, err := strconv.Atoi(params["replicas"])
if err != nil {
return nil, err
}
podSpec, err := makePodSpec(params, name)
if err != nil {
return nil, err
}
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
if err := updatePodPorts(params, podSpec); err != nil {
return nil, err
}
count32 := int32(count)
deployment := appsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: appsv1beta1.DeploymentSpec{
Replicas: &count32,
Selector: &metav1.LabelSelector{MatchLabels: labels},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: *podSpec,
},
},
}
return &deployment, nil
}
// getLabels returns map of labels.
func getLabels(params map[string]string, defaultRunLabel bool, name string) (map[string]string, error) {
labelString, found := params["labels"]
var labels map[string]string
@ -135,6 +227,7 @@ func getLabels(params map[string]string, defaultRunLabel bool, name string) (map
return labels, nil
}
// getName returns the name of newly created resource.
func getName(params map[string]string) (string, error) {
name, found := params["name"]
if !found || len(name) == 0 {
@ -146,6 +239,7 @@ func getName(params map[string]string) (string, error) {
return name, nil
}
// getParams returns map of generic parameters.
func getParams(genericParams map[string]interface{}) (map[string]string, error) {
params := map[string]string{}
for key, value := range genericParams {
@ -158,6 +252,7 @@ func getParams(genericParams map[string]interface{}) (map[string]string, error)
return params, nil
}
// getArgs returns arguments for the container command.
func getArgs(genericParams map[string]interface{}) ([]string, error) {
args := []string{}
val, found := genericParams["args"]
@ -172,8 +267,9 @@ func getArgs(genericParams map[string]interface{}) ([]string, error) {
return args, nil
}
func getEnvs(genericParams map[string]interface{}) ([]api.EnvVar, error) {
var envs []api.EnvVar
// getEnvs returns environment variables.
func getEnvs(genericParams map[string]interface{}) ([]v1.EnvVar, error) {
var envs []v1.EnvVar
envStrings, found := genericParams["env"]
if found {
if envStringArray, isArray := envStrings.([]string); isArray {
@ -244,7 +340,7 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return nil, err
}
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -259,19 +355,19 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return nil, err
}
restartPolicy := api.RestartPolicy(params["restart"])
restartPolicy := v1.RestartPolicy(params["restart"])
if len(restartPolicy) == 0 {
restartPolicy = api.RestartPolicyNever
restartPolicy = v1.RestartPolicyNever
}
podSpec.RestartPolicy = restartPolicy
job := batch.Job{
job := batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
@ -338,7 +434,7 @@ func (CronJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.O
return nil, err
}
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -353,23 +449,23 @@ func (CronJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.O
return nil, err
}
restartPolicy := api.RestartPolicy(params["restart"])
restartPolicy := v1.RestartPolicy(params["restart"])
if len(restartPolicy) == 0 {
restartPolicy = api.RestartPolicyNever
restartPolicy = v1.RestartPolicyNever
}
podSpec.RestartPolicy = restartPolicy
cronJob := batch.CronJob{
cronJob := batchv2alpha1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: batch.CronJobSpec{
Spec: batchv2alpha1.CronJobSpec{
Schedule: params["schedule"],
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{
ConcurrencyPolicy: batchv2alpha1.AllowConcurrent,
JobTemplate: batchv2alpha1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
@ -406,6 +502,7 @@ func (BasicReplicationController) ParamNames() []GeneratorParam {
}
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
// and returns ResourceList.
func populateResourceList(spec string) (api.ResourceList, error) {
// empty input gets a nil response to preserve generator test expected behaviors
if spec == "" {
@ -429,7 +526,33 @@ func populateResourceList(spec string) (api.ResourceList, error) {
return result, nil
}
// populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
// and returns ResourceList.
func populateResourceListV1(spec string) (v1.ResourceList, error) {
// empty input gets a nil response to preserve generator test expected behaviors
if spec == "" {
return nil, nil
}
result := v1.ResourceList{}
resourceStatements := strings.Split(spec, ",")
for _, resourceStatement := range resourceStatements {
parts := strings.Split(resourceStatement, "=")
if len(parts) != 2 {
return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
}
resourceName := v1.ResourceName(parts[0])
resourceQuantity, err := resource.ParseQuantity(parts[1])
if err != nil {
return nil, err
}
result[resourceName] = resourceQuantity
}
return result, nil
}
// HandleResourceRequirements parses the limits and requests parameters if specified
// and returns ResourceRequirements.
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
result := api.ResourceRequirements{}
limits, err := populateResourceList(params["limits"])
@ -445,7 +568,25 @@ func HandleResourceRequirements(params map[string]string) (api.ResourceRequireme
return result, nil
}
func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
// HandleResourceRequirementsV1 parses the limits and requests parameters if specified
// and returns ResourceRequirements.
func HandleResourceRequirementsV1(params map[string]string) (v1.ResourceRequirements, error) {
result := v1.ResourceRequirements{}
limits, err := populateResourceListV1(params["limits"])
if err != nil {
return result, err
}
result.Limits = limits
requests, err := populateResourceListV1(params["requests"])
if err != nil {
return result, err
}
result.Requests = requests
return result, nil
}
// makePodSpec returns PodSpec filled with passed parameters.
func makePodSpec(params map[string]string, name string) (*v1.PodSpec, error) {
stdin, err := GetBool(params, "stdin", false)
if err != nil {
return nil, err
@ -456,13 +597,13 @@ func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
return nil, err
}
resourceRequirements, err := HandleResourceRequirements(params)
resourceRequirements, err := HandleResourceRequirementsV1(params)
if err != nil {
return nil, err
}
spec := api.PodSpec{
Containers: []api.Container{
spec := v1.PodSpec{
Containers: []v1.Container{
{
Name: name,
Image: params["image"],
@ -511,7 +652,7 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
return nil, err
}
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -520,15 +661,16 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
return nil, err
}
controller := api.ReplicationController{
count32 := int32(count)
controller := v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: api.ReplicationControllerSpec{
Replicas: int32(count),
Spec: v1.ReplicationControllerSpec{
Replicas: &count32,
Selector: labels,
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
@ -539,7 +681,8 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
return &controller, nil
}
func updatePodContainers(params map[string]string, args []string, envs []api.EnvVar, imagePullPolicy api.PullPolicy, podSpec *api.PodSpec) error {
// updatePodContainers updates PodSpec.Containers with passed parameters.
func updatePodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error {
if len(args) > 0 {
command, err := GetBool(params, "command", false)
if err != nil {
@ -563,7 +706,8 @@ func updatePodContainers(params map[string]string, args []string, envs []api.Env
return nil
}
func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) {
// updatePodContainers updates PodSpec.Containers.Ports with passed parameters.
func updatePodPorts(params map[string]string, podSpec *v1.PodSpec) (err error) {
port := -1
hostPort := -1
if len(params["port"]) > 0 {
@ -585,7 +729,7 @@ func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error)
// Don't include the port if it was not specified.
if len(params["port"]) > 0 {
podSpec.Containers[0].Ports = []api.ContainerPort{
podSpec.Containers[0].Ports = []v1.ContainerPort{
{
ContainerPort: int32(port),
},
@ -660,39 +804,39 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
return nil, err
}
resourceRequirements, err := HandleResourceRequirements(params)
resourceRequirements, err := HandleResourceRequirementsV1(params)
if err != nil {
return nil, err
}
restartPolicy := api.RestartPolicy(params["restart"])
restartPolicy := v1.RestartPolicy(params["restart"])
if len(restartPolicy) == 0 {
restartPolicy = api.RestartPolicyAlways
restartPolicy = v1.RestartPolicyAlways
}
// TODO: Figure out why we set ImagePullPolicy here, whether we can make it
// consistent with the other places imagePullPolicy is set using flag.
pod := api.Pod{
pod := v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: name,
Image: params["image"],
ImagePullPolicy: api.PullIfNotPresent,
ImagePullPolicy: v1.PullIfNotPresent,
Stdin: stdin,
StdinOnce: !leaveStdinOpen && stdin,
TTY: tty,
Resources: resourceRequirements,
},
},
DNSPolicy: api.DNSClusterFirst,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: restartPolicy,
},
}
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil {
return nil, err
}
@ -703,8 +847,9 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
return &pod, nil
}
func parseEnvs(envArray []string) ([]api.EnvVar, error) {
envs := make([]api.EnvVar, 0, len(envArray))
// parseEnvs converts string into EnvVar objects.
func parseEnvs(envArray []string) ([]v1.EnvVar, error) {
envs := make([]v1.EnvVar, 0, len(envArray))
for _, env := range envArray {
pos := strings.Index(env, "=")
if pos == -1 {
@ -718,7 +863,7 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) {
if len(validation.IsCIdentifier(name)) != 0 {
return nil, fmt.Errorf("invalid env: %v", env)
}
envVar := api.EnvVar{Name: name, Value: value}
envVar := v1.EnvVar{Name: name, Value: value}
envs = append(envs, envVar)
}
return envs, nil

View File

@ -22,15 +22,18 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/api/v1"
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)
func TestGenerate(t *testing.T) {
one := int32(1)
tests := []struct {
params map[string]interface{}
expected *api.ReplicationController
expected *v1.ReplicationController
expectErr bool
}{
{
@ -41,24 +44,24 @@ func TestGenerate(t *testing.T) {
"replicas": "1",
"port": "",
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullAlways,
ImagePullPolicy: v1.PullAlways,
},
},
},
@ -75,24 +78,24 @@ func TestGenerate(t *testing.T) {
"port": "",
"env": []string{"a=b", "c=d"},
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
Env: []api.EnvVar{
Env: []v1.EnvVar{
{
Name: "a",
Value: "b",
@ -119,24 +122,24 @@ func TestGenerate(t *testing.T) {
"port": "",
"args": []string{"bar", "baz", "blah"},
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullNever,
ImagePullPolicy: v1.PullNever,
Args: []string{"bar", "baz", "blah"},
},
},
@ -154,20 +157,20 @@ func TestGenerate(t *testing.T) {
"args": []string{"bar", "baz", "blah"},
"command": "true",
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
@ -186,24 +189,24 @@ func TestGenerate(t *testing.T) {
"replicas": "1",
"port": "80",
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
Ports: []api.ContainerPort{
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
},
@ -224,25 +227,25 @@ func TestGenerate(t *testing.T) {
"port": "80",
"hostport": "80",
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"run": "foo"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"run": "foo"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"run": "foo"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
Ports: []api.ContainerPort{
ImagePullPolicy: v1.PullIfNotPresent,
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
HostPort: 80,
@ -272,20 +275,20 @@ func TestGenerate(t *testing.T) {
"replicas": "1",
"labels": "foo=bar,baz=blah",
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"foo": "bar", "baz": "blah"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
@ -348,31 +351,31 @@ func TestGenerate(t *testing.T) {
"requests": "cpu=100m,memory=100Mi",
"limits": "cpu=400m,memory=200Mi",
},
expected: &api.ReplicationController{
expected: &v1.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Spec: v1.ReplicationControllerSpec{
Replicas: &one,
Selector: map[string]string{"foo": "bar", "baz": "blah"},
Template: &api.PodTemplateSpec{
Template: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("100Mi"),
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
Limits: api.ResourceList{
api.ResourceCPU: resource.MustParse("400m"),
api.ResourceMemory: resource.MustParse("200Mi"),
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("400m"),
v1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
@ -394,8 +397,8 @@ func TestGenerate(t *testing.T) {
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*api.ReplicationController).Spec.Template, test.expected.Spec.Template) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected.Spec.Template, obj.(*api.ReplicationController).Spec.Template)
if !reflect.DeepEqual(obj.(*v1.ReplicationController).Spec.Template, test.expected.Spec.Template) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected.Spec.Template, obj.(*v1.ReplicationController).Spec.Template)
}
}
}
@ -403,7 +406,7 @@ func TestGenerate(t *testing.T) {
func TestGeneratePod(t *testing.T) {
tests := []struct {
params map[string]interface{}
expected *api.Pod
expected *v1.Pod
expectErr bool
}{
{
@ -412,20 +415,20 @@ func TestGeneratePod(t *testing.T) {
"image": "someimage",
"port": "",
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
ImagePullPolicy: v1.PullIfNotPresent,
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -446,17 +449,17 @@ func TestGeneratePod(t *testing.T) {
"image-pull-policy": "Always",
"env": []string{"a=b", "c=d"},
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullAlways,
Env: []api.EnvVar{
ImagePullPolicy: v1.PullAlways,
Env: []v1.EnvVar{
{
Name: "a",
Value: "b",
@ -468,8 +471,8 @@ func TestGeneratePod(t *testing.T) {
},
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -479,25 +482,25 @@ func TestGeneratePod(t *testing.T) {
"image": "someimage",
"port": "80",
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
Ports: []api.ContainerPort{
ImagePullPolicy: v1.PullIfNotPresent,
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
},
},
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -508,17 +511,17 @@ func TestGeneratePod(t *testing.T) {
"port": "80",
"hostport": "80",
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
Ports: []api.ContainerPort{
ImagePullPolicy: v1.PullIfNotPresent,
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
HostPort: 80,
@ -526,8 +529,8 @@ func TestGeneratePod(t *testing.T) {
},
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -547,21 +550,21 @@ func TestGeneratePod(t *testing.T) {
"replicas": "1",
"labels": "foo=bar,baz=blah",
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
ImagePullPolicy: v1.PullIfNotPresent,
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -573,23 +576,23 @@ func TestGeneratePod(t *testing.T) {
"labels": "foo=bar,baz=blah",
"stdin": "true",
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
ImagePullPolicy: v1.PullIfNotPresent,
Stdin: true,
StdinOnce: true,
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -602,23 +605,23 @@ func TestGeneratePod(t *testing.T) {
"stdin": "true",
"leave-stdin-open": "true",
},
expected: &api.Pod{
expected: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullIfNotPresent,
ImagePullPolicy: v1.PullIfNotPresent,
Stdin: true,
StdinOnce: false,
},
},
DNSPolicy: api.DNSClusterFirst,
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: v1.DNSClusterFirst,
RestartPolicy: v1.RestartPolicyAlways,
},
},
},
@ -632,16 +635,17 @@ func TestGeneratePod(t *testing.T) {
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*api.Pod), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Pod))
if !reflect.DeepEqual(obj.(*v1.Pod), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*v1.Pod))
}
}
}
func TestGenerateDeployment(t *testing.T) {
three := int32(3)
tests := []struct {
params map[string]interface{}
expected *extensions.Deployment
expected *extensionsv1beta1.Deployment
expectErr bool
}{
{
@ -660,33 +664,33 @@ func TestGenerateDeployment(t *testing.T) {
"requests": "cpu=100m,memory=100Mi",
"limits": "cpu=400m,memory=200Mi",
},
expected: &extensions.Deployment{
expected: &extensionsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: extensions.DeploymentSpec{
Replicas: 3,
Spec: extensionsv1beta1.DeploymentSpec{
Replicas: &three,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar", "baz": "blah"}},
Template: api.PodTemplateSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
Containers: []api.Container{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: api.PullAlways,
ImagePullPolicy: v1.PullAlways,
Stdin: true,
Ports: []api.ContainerPort{
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
HostPort: 80,
},
},
Command: []string{"bar", "baz", "blah"},
Env: []api.EnvVar{
Env: []v1.EnvVar{
{
Name: "a",
Value: "b",
@ -696,14 +700,14 @@ func TestGenerateDeployment(t *testing.T) {
Value: "d",
},
},
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("100Mi"),
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
Limits: api.ResourceList{
api.ResourceCPU: resource.MustParse("400m"),
api.ResourceMemory: resource.MustParse("200Mi"),
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("400m"),
v1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
@ -724,8 +728,101 @@ func TestGenerateDeployment(t *testing.T) {
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*extensions.Deployment), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensions.Deployment))
if !reflect.DeepEqual(obj.(*extensionsv1beta1.Deployment), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensionsv1beta1.Deployment))
}
}
}
func TestGenerateAppsDeployment(t *testing.T) {
three := int32(3)
tests := []struct {
params map[string]interface{}
expected *appsv1beta1.Deployment
expectErr bool
}{
{
params: map[string]interface{}{
"labels": "foo=bar,baz=blah",
"name": "foo",
"replicas": "3",
"image": "someimage",
"image-pull-policy": "Always",
"port": "80",
"hostport": "80",
"stdin": "true",
"command": "true",
"args": []string{"bar", "baz", "blah"},
"env": []string{"a=b", "c=d"},
"requests": "cpu=100m,memory=100Mi",
"limits": "cpu=400m,memory=200Mi",
},
expected: &appsv1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: appsv1beta1.DeploymentSpec{
Replicas: &three,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar", "baz": "blah"}},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
ImagePullPolicy: v1.PullAlways,
Stdin: true,
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
HostPort: 80,
},
},
Command: []string{"bar", "baz", "blah"},
Env: []v1.EnvVar{
{
Name: "a",
Value: "b",
},
{
Name: "c",
Value: "d",
},
},
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("400m"),
v1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
},
},
},
},
},
},
}
generator := DeploymentAppsV1Beta1{}
for _, test := range tests {
obj, err := generator.Generate(test.params)
if !test.expectErr && err != nil {
t.Errorf("unexpected error: %v", err)
}
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*appsv1beta1.Deployment), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*appsv1beta1.Deployment))
}
}
}
@ -733,7 +830,7 @@ func TestGenerateDeployment(t *testing.T) {
func TestGenerateJob(t *testing.T) {
tests := []struct {
params map[string]interface{}
expected *batch.Job
expected *batchv1.Job
expectErr bool
}{
{
@ -752,32 +849,32 @@ func TestGenerateJob(t *testing.T) {
"limits": "cpu=400m,memory=200Mi",
"restart": "OnFailure",
},
expected: &batch.Job{
expected: &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
Containers: []api.Container{
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyOnFailure,
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
Stdin: true,
StdinOnce: false,
Ports: []api.ContainerPort{
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
HostPort: 80,
},
},
Command: []string{"bar", "baz", "blah"},
Env: []api.EnvVar{
Env: []v1.EnvVar{
{
Name: "a",
Value: "b",
@ -787,14 +884,14 @@ func TestGenerateJob(t *testing.T) {
Value: "d",
},
},
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("100Mi"),
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
Limits: api.ResourceList{
api.ResourceCPU: resource.MustParse("400m"),
api.ResourceMemory: resource.MustParse("200Mi"),
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("400m"),
v1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
@ -815,8 +912,8 @@ func TestGenerateJob(t *testing.T) {
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*batch.Job), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*batch.Job))
if !reflect.DeepEqual(obj.(*batchv1.Job), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*batchv1.Job))
}
}
}
@ -824,7 +921,7 @@ func TestGenerateJob(t *testing.T) {
func TestGenerateCronJob(t *testing.T) {
tests := []struct {
params map[string]interface{}
expected *batch.CronJob
expected *batchv2alpha1.CronJob
expectErr bool
}{
{
@ -844,36 +941,36 @@ func TestGenerateCronJob(t *testing.T) {
"restart": "OnFailure",
"schedule": "0/5 * * * ?",
},
expected: &batch.CronJob{
expected: &batchv2alpha1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: batch.CronJobSpec{
Spec: batchv2alpha1.CronJobSpec{
Schedule: "0/5 * * * ?",
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{
ConcurrencyPolicy: batchv2alpha1.AllowConcurrent,
JobTemplate: batchv2alpha1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
Containers: []api.Container{
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyOnFailure,
Containers: []v1.Container{
{
Name: "foo",
Image: "someimage",
Stdin: true,
StdinOnce: false,
Ports: []api.ContainerPort{
Ports: []v1.ContainerPort{
{
ContainerPort: 80,
HostPort: 80,
},
},
Command: []string{"bar", "baz", "blah"},
Env: []api.EnvVar{
Env: []v1.EnvVar{
{
Name: "a",
Value: "b",
@ -883,14 +980,14 @@ func TestGenerateCronJob(t *testing.T) {
Value: "d",
},
},
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("100Mi"),
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("100m"),
v1.ResourceMemory: resource.MustParse("100Mi"),
},
Limits: api.ResourceList{
api.ResourceCPU: resource.MustParse("400m"),
api.ResourceMemory: resource.MustParse("200Mi"),
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("400m"),
v1.ResourceMemory: resource.MustParse("200Mi"),
},
},
},
@ -913,8 +1010,8 @@ func TestGenerateCronJob(t *testing.T) {
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*batch.CronJob), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*batch.CronJob))
if !reflect.DeepEqual(obj.(*batchv2alpha1.CronJob), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*batchv2alpha1.CronJob))
}
}
}
@ -922,7 +1019,7 @@ func TestGenerateCronJob(t *testing.T) {
func TestParseEnv(t *testing.T) {
tests := []struct {
envArray []string
expected []api.EnvVar
expected []v1.EnvVar
expectErr bool
test string
}{
@ -932,7 +1029,7 @@ func TestParseEnv(t *testing.T) {
"HAS_COMMAS=foo,bar",
"HAS_EQUALS=jJnro54iUu75xNy==",
},
expected: []api.EnvVar{
expected: []v1.EnvVar{
{
Name: "THIS_ENV",
Value: "isOK",
@ -953,7 +1050,7 @@ func TestParseEnv(t *testing.T) {
envArray: []string{
"WITH_OUT_EQUALS",
},
expected: []api.EnvVar{},
expected: []v1.EnvVar{},
expectErr: true,
test: "test case 2",
},
@ -961,7 +1058,7 @@ func TestParseEnv(t *testing.T) {
envArray: []string{
"WITH_OUT_VALUES=",
},
expected: []api.EnvVar{
expected: []v1.EnvVar{
{
Name: "WITH_OUT_VALUES",
Value: "",
@ -974,7 +1071,7 @@ func TestParseEnv(t *testing.T) {
envArray: []string{
"=WITH_OUT_NAME",
},
expected: []api.EnvVar{},
expected: []v1.EnvVar{},
expectErr: true,
test: "test case 4",
},