Merge pull request #91113 from soltysh/create_deployment

Refactor create deployment and add --port flag
This commit is contained in:
Kubernetes Prow Robot 2020-05-26 23:24:54 -07:00 committed by GitHub
commit 7c3f7065db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 49 deletions

View File

@ -80,14 +80,14 @@ func NewCreateCronJobOptions(ioStreams genericclioptions.IOStreams) *CreateCronJ
func NewCmdCreateCronJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { func NewCmdCreateCronJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCreateCronJobOptions(ioStreams) o := NewCreateCronJobOptions(ioStreams)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "cronjob NAME --image=image --schedule='0/5 * * * ?' -- [COMMAND] [args...]", Use: "cronjob NAME --image=image --schedule='0/5 * * * ?' -- [COMMAND] [args...]",
Aliases: []string{"cj"}, DisableFlagsInUseLine: false,
Short: cronjobLong, Aliases: []string{"cj"},
Long: cronjobLong, Short: cronjobLong,
Example: cronjobExample, Long: cronjobLong,
Example: cronjobExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run()) cmdutil.CheckErr(o.Run())
}, },
} }
@ -98,7 +98,9 @@ func NewCmdCreateCronJob(f cmdutil.Factory, ioStreams genericclioptions.IOStream
cmdutil.AddValidateFlags(cmd) cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd) cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.") cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
cmd.MarkFlagRequired("image")
cmd.Flags().StringVar(&o.Schedule, "schedule", o.Schedule, "A schedule in the Cron format the job should be run with.") cmd.Flags().StringVar(&o.Schedule, "schedule", o.Schedule, "A schedule in the Cron format the job should be run with.")
cmd.MarkFlagRequired("schedule")
cmd.Flags().StringVar(&o.Restart, "restart", o.Restart, "job's restart policy. supported values: OnFailure, Never") cmd.Flags().StringVar(&o.Restart, "restart", o.Restart, "job's restart policy. supported values: OnFailure, Never")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create") cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
@ -158,20 +160,8 @@ func (o *CreateCronJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
return nil return nil
} }
func (o *CreateCronJobOptions) Validate() error {
if len(o.Image) == 0 {
return fmt.Errorf("--image must be specified")
}
if len(o.Schedule) == 0 {
return fmt.Errorf("--schedule must be specified")
}
return nil
}
func (o *CreateCronJobOptions) Run() error { func (o *CreateCronJobOptions) Run() error {
var cronjob *batchv1beta1.CronJob cronjob := o.createCronJob()
cronjob = o.createCronJob()
if o.DryRunStrategy != cmdutil.DryRunClient { if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{} createOptions := metav1.CreateOptions{}
if o.FieldManager != "" { if o.FieldManager != "" {

View File

@ -22,8 +22,9 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilrand "k8s.io/apimachinery/pkg/util/rand" utilrand "k8s.io/apimachinery/pkg/util/rand"
@ -41,17 +42,26 @@ var (
Create a deployment with the specified name.`)) Create a deployment with the specified name.`))
deploymentExample = templates.Examples(i18n.T(` deploymentExample = templates.Examples(i18n.T(`
# Create a new deployment named my-dep that runs the busybox image. # Create a deployment named my-dep that runs the busybox image.
kubectl create deployment my-dep --image=busybox`)) kubectl create deployment my-dep --image=busybox
# Create a deployment with command
kubectl create deployment my-dep --image=busybox -- date
# Create a deployment named my-dep that runs the busybox image and expose port 5701.
kubectl create deployment my-dep --image=busybox --port=5701`))
) )
// DeploymentOpts is returned by NewCmdCreateDeployment // CreateDeploymentOptions is returned by NewCmdCreateDeployment
type DeploymentOpts struct { type CreateDeploymentOptions struct {
PrintFlags *genericclioptions.PrintFlags PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
PrintObj func(obj runtime.Object) error
Name string Name string
Images []string Images []string
Port int32
Command []string
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
FieldManager string FieldManager string
@ -63,46 +73,55 @@ type DeploymentOpts struct {
genericclioptions.IOStreams genericclioptions.IOStreams
} }
// NewCmdCreateDeployment is a macro command to create a new deployment. func NewCreateCreateDeploymentOptions(ioStreams genericclioptions.IOStreams) *CreateDeploymentOptions {
// This command is better known to users as `kubectl create deployment`. return &CreateDeploymentOptions{
func NewCmdCreateDeployment(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { Port: -1,
options := &DeploymentOpts{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme), PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams, IOStreams: ioStreams,
} }
}
// NewCmdCreateDeployment is a macro command to create a new deployment.
// This command is better known to users as `kubectl create deployment`.
func NewCmdCreateDeployment(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCreateCreateDeploymentOptions(ioStreams)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "deployment NAME --image=image [--dry-run=server|client|none]", Use: "deployment NAME --image=image -- [COMMAND] [args...]",
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Aliases: []string{"deploy"}, Aliases: []string{"deploy"},
Short: deploymentLong, Short: deploymentLong,
Long: deploymentLong, Long: deploymentLong,
Example: deploymentExample, Example: deploymentExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(options.Run()) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
}, },
} }
options.PrintFlags.AddFlags(cmd) o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd) cmdutil.AddValidateFlags(cmd)
cmdutil.AddGeneratorFlags(cmd, "") cmdutil.AddGeneratorFlags(cmd, "")
cmd.Flags().StringSliceVar(&options.Images, "image", []string{}, "Image name to run.") cmd.Flags().StringSliceVar(&o.Images, "image", o.Images, "Image names to run.")
_ = cmd.MarkFlagRequired("image") cmd.MarkFlagRequired("image")
cmdutil.AddFieldManagerFlagVar(cmd, &options.FieldManager, "kubectl-create") cmd.Flags().Int32Var(&o.Port, "port", o.Port, "The port that this container exposes.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd return cmd
} }
// Complete completes all the options // Complete completes all the options
func (o *DeploymentOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { func (o *CreateDeploymentOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args) name, err := NameFromCommandArgs(cmd, args)
if err != nil { if err != nil {
return err return err
} }
o.Name = name o.Name = name
if len(args) > 1 {
o.Command = args[1:]
}
clientConfig, err := f.ToRESTConfig() clientConfig, err := f.ToRESTConfig()
if err != nil { if err != nil {
@ -144,8 +163,15 @@ func (o *DeploymentOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return nil return nil
} }
func (o *CreateDeploymentOptions) Validate() error {
if len(o.Images) > 1 && len(o.Command) > 0 {
return fmt.Errorf("cannot specify multiple --image options and command")
}
return nil
}
// Run performs the execution of 'create deployment' sub command // Run performs the execution of 'create deployment' sub command
func (o *DeploymentOpts) Run() error { func (o *CreateDeploymentOptions) Run() error {
deploy := o.createDeployment() deploy := o.createDeployment()
if o.DryRunStrategy != cmdutil.DryRunClient { if o.DryRunStrategy != cmdutil.DryRunClient {
@ -169,7 +195,7 @@ func (o *DeploymentOpts) Run() error {
return o.PrintObj(deploy) return o.PrintObj(deploy)
} }
func (o *DeploymentOpts) createDeployment() *appsv1.Deployment { func (o *CreateDeploymentOptions) createDeployment() *appsv1.Deployment {
one := int32(1) one := int32(1)
labels := map[string]string{"app": o.Name} labels := map[string]string{"app": o.Name}
selector := metav1.LabelSelector{MatchLabels: labels} selector := metav1.LabelSelector{MatchLabels: labels}
@ -188,7 +214,7 @@ func (o *DeploymentOpts) createDeployment() *appsv1.Deployment {
Spec: appsv1.DeploymentSpec{ Spec: appsv1.DeploymentSpec{
Replicas: &one, Replicas: &one,
Selector: &selector, Selector: &selector,
Template: v1.PodTemplateSpec{ Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: labels, Labels: labels,
}, },
@ -200,8 +226,8 @@ func (o *DeploymentOpts) createDeployment() *appsv1.Deployment {
// buildPodSpec parses the image strings and assemble them into the Containers // buildPodSpec parses the image strings and assemble them into the Containers
// of a PodSpec. This is all you need to create the PodSpec for a deployment. // of a PodSpec. This is all you need to create the PodSpec for a deployment.
func (o *DeploymentOpts) buildPodSpec() v1.PodSpec { func (o *CreateDeploymentOptions) buildPodSpec() corev1.PodSpec {
podSpec := v1.PodSpec{Containers: []v1.Container{}} podSpec := corev1.PodSpec{Containers: []corev1.Container{}}
for _, imageString := range o.Images { for _, imageString := range o.Images {
// Retain just the image name // Retain just the image name
imageSplit := strings.Split(imageString, "/") imageSplit := strings.Split(imageString, "/")
@ -214,7 +240,11 @@ func (o *DeploymentOpts) buildPodSpec() v1.PodSpec {
name = strings.Split(name, "@")[0] name = strings.Split(name, "@")[0]
} }
name = sanitizeAndUniquify(name) name = sanitizeAndUniquify(name)
podSpec.Containers = append(podSpec.Containers, v1.Container{Name: name, Image: imageString}) podSpec.Containers = append(podSpec.Containers, corev1.Container{
Name: name,
Image: imageString,
Command: o.Command,
})
} }
return podSpec return podSpec
} }

View File

@ -83,7 +83,7 @@ func TestCreateDeploymentNoImage(t *testing.T) {
ioStreams := genericclioptions.NewTestIOStreamsDiscard() ioStreams := genericclioptions.NewTestIOStreamsDiscard()
cmd := NewCmdCreateDeployment(tf, ioStreams) cmd := NewCmdCreateDeployment(tf, ioStreams)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
options := &DeploymentOpts{ options := &CreateDeploymentOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme), PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
DryRunStrategy: cmdutil.DryRunClient, DryRunStrategy: cmdutil.DryRunClient,
IOStreams: ioStreams, IOStreams: ioStreams,

View File

@ -85,10 +85,11 @@ func NewCreateJobOptions(ioStreams genericclioptions.IOStreams) *CreateJobOption
func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCreateJobOptions(ioStreams) o := NewCreateJobOptions(ioStreams)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "job NAME --image=image [--from=cronjob/name] -- [COMMAND] [args...]", Use: "job NAME --image=image [--from=cronjob/name] -- [COMMAND] [args...]",
Short: jobLong, DisableFlagsInUseLine: true,
Long: jobLong, Short: jobLong,
Example: jobExample, Long: jobLong,
Example: jobExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Validate())

View File

@ -197,7 +197,7 @@ run_deployment_tests() {
kubectl delete deployment test-nginx-extensions "${kube_flags[@]:?}" kubectl delete deployment test-nginx-extensions "${kube_flags[@]:?}"
# Test kubectl create deployment # Test kubectl create deployment
kubectl create deployment test-nginx-apps --image=k8s.gcr.io/nginx:test-cmd --generator=deployment-basic/apps.v1 kubectl create deployment test-nginx-apps --image=k8s.gcr.io/nginx:test-cmd
# Post-Condition: Deployment "nginx" is created. # Post-Condition: Deployment "nginx" is created.
kube::test::get_object_assert 'deploy test-nginx-apps' "{{${container_name_field:?}}}" 'nginx' kube::test::get_object_assert 'deploy test-nginx-apps' "{{${container_name_field:?}}}" 'nginx'
# and new generator was used, iow. new defaults are applied # and new generator was used, iow. new defaults are applied