@@ -17,12 +17,21 @@ limitations under the License.
17
17
package create
18
18
19
19
import (
20
- "github.com/spf13/cobra"
20
+ "context"
21
+ "fmt"
22
+ "strings"
21
23
24
+ "github.com/spf13/cobra"
25
+ appsv1 "k8s.io/api/apps/v1"
26
+ v1 "k8s.io/api/core/v1"
27
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
+ "k8s.io/apimachinery/pkg/runtime"
29
+ utilrand "k8s.io/apimachinery/pkg/util/rand"
22
30
"k8s.io/cli-runtime/pkg/genericclioptions"
31
+ "k8s.io/cli-runtime/pkg/resource"
32
+ appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
23
33
cmdutil "k8s.io/kubectl/pkg/cmd/util"
24
- "k8s.io/kubectl/pkg/generate"
25
- generateversioned "k8s.io/kubectl/pkg/generate/versioned"
34
+ "k8s.io/kubectl/pkg/scheme"
26
35
"k8s.io/kubectl/pkg/util/i18n"
27
36
"k8s.io/kubectl/pkg/util/templates"
28
37
)
@@ -38,22 +47,35 @@ var (
38
47
39
48
// DeploymentOpts is returned by NewCmdCreateDeployment
40
49
type DeploymentOpts struct {
41
- CreateSubcommandOptions * CreateSubcommandOptions
50
+ PrintFlags * genericclioptions.PrintFlags
51
+ PrintObj func (obj runtime.Object ) error
52
+
53
+ Name string
54
+ Images []string
55
+ Namespace string
56
+ EnforceNamespace bool
57
+ FieldManager string
58
+
59
+ Client appsv1client.AppsV1Interface
60
+ DryRunStrategy cmdutil.DryRunStrategy
61
+ DryRunVerifier * resource.DryRunVerifier
62
+
63
+ genericclioptions.IOStreams
42
64
}
43
65
44
66
// NewCmdCreateDeployment is a macro command to create a new deployment.
45
67
// This command is better known to users as `kubectl create deployment`.
46
- // Note that this command overlaps significantly with the `kubectl run` command.
47
68
func NewCmdCreateDeployment (f cmdutil.Factory , ioStreams genericclioptions.IOStreams ) * cobra.Command {
48
69
options := & DeploymentOpts {
49
- CreateSubcommandOptions : NewCreateSubcommandOptions (ioStreams ),
70
+ PrintFlags : genericclioptions .NewPrintFlags ("created" ).WithTypeSetter (scheme .Scheme ),
71
+ IOStreams : ioStreams ,
50
72
}
51
73
52
74
cmd := & cobra.Command {
53
75
Use : "deployment NAME --image=image [--dry-run=server|client|none]" ,
54
76
DisableFlagsInUseLine : true ,
55
77
Aliases : []string {"deploy" },
56
- Short : i18n . T ( "Create a deployment with the specified name." ) ,
78
+ Short : deploymentLong ,
57
79
Long : deploymentLong ,
58
80
Example : deploymentExample ,
59
81
Run : func (cmd * cobra.Command , args []string ) {
@@ -62,56 +84,16 @@ func NewCmdCreateDeployment(f cmdutil.Factory, ioStreams genericclioptions.IOStr
62
84
},
63
85
}
64
86
65
- options .CreateSubcommandOptions . PrintFlags .AddFlags (cmd )
87
+ options .PrintFlags .AddFlags (cmd )
66
88
67
89
cmdutil .AddApplyAnnotationFlags (cmd )
68
90
cmdutil .AddValidateFlags (cmd )
69
91
cmdutil .AddGeneratorFlags (cmd , "" )
70
- cmd .Flags ().StringSlice ("image" , []string {}, "Image name to run." )
71
- cmd .MarkFlagRequired ("image" )
72
- cmdutil .AddFieldManagerFlagVar (cmd , & options .CreateSubcommandOptions .FieldManager , "kubectl-create" )
73
- return cmd
74
- }
75
-
76
- // generatorFromName returns the appropriate StructuredGenerator based on the
77
- // generatorName. If the generatorName is unrecognized, then return (nil,
78
- // false).
79
- func generatorFromName (
80
- generatorName string ,
81
- imageNames []string ,
82
- deploymentName string ,
83
- ) (generate.StructuredGenerator , bool ) {
84
-
85
- switch generatorName {
86
- case generateversioned .DeploymentBasicAppsV1GeneratorName :
87
- generator := & generateversioned.DeploymentBasicAppsGeneratorV1 {
88
- BaseDeploymentGenerator : generateversioned.BaseDeploymentGenerator {
89
- Name : deploymentName ,
90
- Images : imageNames ,
91
- },
92
- }
93
- return generator , true
94
-
95
- case generateversioned .DeploymentBasicAppsV1Beta1GeneratorName :
96
- generator := & generateversioned.DeploymentBasicAppsGeneratorV1Beta1 {
97
- BaseDeploymentGenerator : generateversioned.BaseDeploymentGenerator {
98
- Name : deploymentName ,
99
- Images : imageNames ,
100
- },
101
- }
102
- return generator , true
103
-
104
- case generateversioned .DeploymentBasicV1Beta1GeneratorName :
105
- generator := & generateversioned.DeploymentBasicGeneratorV1 {
106
- BaseDeploymentGenerator : generateversioned.BaseDeploymentGenerator {
107
- Name : deploymentName ,
108
- Images : imageNames ,
109
- },
110
- }
111
- return generator , true
112
- }
92
+ cmd .Flags ().StringSliceVar (& options .Images , "image" , []string {}, "Image name to run." )
93
+ _ = cmd .MarkFlagRequired ("image" )
94
+ cmdutil .AddFieldManagerFlagVar (cmd , & options .FieldManager , "kubectl-create" )
113
95
114
- return nil , false
96
+ return cmd
115
97
}
116
98
117
99
// Complete completes all the options
@@ -120,37 +102,126 @@ func (o *DeploymentOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
120
102
if err != nil {
121
103
return err
122
104
}
105
+ o .Name = name
123
106
124
- clientset , err := f .KubernetesClientSet ()
107
+ clientConfig , err := f .ToRESTConfig ()
108
+ if err != nil {
109
+ return err
110
+ }
111
+ o .Client , err = appsv1client .NewForConfig (clientConfig )
125
112
if err != nil {
126
113
return err
127
114
}
128
115
129
- generatorName := cmdutil .GetFlagString (cmd , "generator" )
116
+ o .Namespace , o .EnforceNamespace , err = f .ToRawKubeConfigLoader ().Namespace ()
117
+ if err != nil {
118
+ return err
119
+ }
130
120
131
- if len (generatorName ) == 0 {
132
- generatorName = generateversioned .DeploymentBasicAppsV1GeneratorName
133
- generatorNameTemp , err := generateversioned .FallbackGeneratorNameIfNecessary (generatorName , clientset .Discovery (), o .CreateSubcommandOptions .ErrOut )
134
- if err != nil {
135
- return err
136
- }
137
- if generatorNameTemp != generatorName {
138
- cmdutil .Warning (o .CreateSubcommandOptions .ErrOut , generatorName , generatorNameTemp )
139
- } else {
140
- generatorName = generatorNameTemp
141
- }
121
+ o .DryRunStrategy , err = cmdutil .GetDryRunStrategy (cmd )
122
+ if err != nil {
123
+ return err
142
124
}
125
+ dynamicClient , err := f .DynamicClient ()
126
+ if err != nil {
127
+ return err
128
+ }
129
+ discoveryClient , err := f .ToDiscoveryClient ()
130
+ if err != nil {
131
+ return err
132
+ }
133
+ o .DryRunVerifier = resource .NewDryRunVerifier (dynamicClient , discoveryClient )
134
+ cmdutil .PrintFlagsWithDryRunStrategy (o .PrintFlags , o .DryRunStrategy )
143
135
144
- imageNames := cmdutil .GetFlagStringSlice (cmd , "image" )
145
- generator , ok := generatorFromName (generatorName , imageNames , name )
146
- if ! ok {
147
- return errUnsupportedGenerator (cmd , generatorName )
136
+ printer , err := o .PrintFlags .ToPrinter ()
137
+ if err != nil {
138
+ return err
139
+ }
140
+ o .PrintObj = func (obj runtime.Object ) error {
141
+ return printer .PrintObj (obj , o .Out )
148
142
}
149
143
150
- return o . CreateSubcommandOptions . Complete ( f , cmd , args , generator )
144
+ return nil
151
145
}
152
146
153
147
// Run performs the execution of 'create deployment' sub command
154
148
func (o * DeploymentOpts ) Run () error {
155
- return o .CreateSubcommandOptions .Run ()
149
+ one := int32 (1 )
150
+ labels := map [string ]string {"app" : o .Name }
151
+ selector := metav1.LabelSelector {MatchLabels : labels }
152
+ namespace := ""
153
+ if o .EnforceNamespace {
154
+ namespace = o .Namespace
155
+ }
156
+
157
+ deploy := & appsv1.Deployment {
158
+ TypeMeta : metav1.TypeMeta {APIVersion : appsv1 .SchemeGroupVersion .String (), Kind : "Deployment" },
159
+ ObjectMeta : metav1.ObjectMeta {
160
+ Name : o .Name ,
161
+ Labels : labels ,
162
+ Namespace : namespace ,
163
+ },
164
+ Spec : appsv1.DeploymentSpec {
165
+ Replicas : & one ,
166
+ Selector : & selector ,
167
+ Template : v1.PodTemplateSpec {
168
+ ObjectMeta : metav1.ObjectMeta {
169
+ Labels : labels ,
170
+ },
171
+ Spec : o .buildPodSpec (),
172
+ },
173
+ },
174
+ }
175
+
176
+ if o .DryRunStrategy != cmdutil .DryRunClient {
177
+ createOptions := metav1.CreateOptions {}
178
+ if o .FieldManager != "" {
179
+ createOptions .FieldManager = o .FieldManager
180
+ }
181
+ if o .DryRunStrategy == cmdutil .DryRunServer {
182
+ if err := o .DryRunVerifier .HasSupport (deploy .GroupVersionKind ()); err != nil {
183
+ return err
184
+ }
185
+ createOptions .DryRun = []string {metav1 .DryRunAll }
186
+ }
187
+ var err error
188
+ deploy , err = o .Client .Deployments (o .Namespace ).Create (context .TODO (), deploy , createOptions )
189
+ if err != nil {
190
+ return fmt .Errorf ("failed to create deployment: %v" , err )
191
+ }
192
+ }
193
+
194
+ return o .PrintObj (deploy )
195
+ }
196
+
197
+ // buildPodSpec parses the image strings and assemble them into the Containers
198
+ // of a PodSpec. This is all you need to create the PodSpec for a deployment.
199
+ func (o * DeploymentOpts ) buildPodSpec () v1.PodSpec {
200
+ podSpec := v1.PodSpec {Containers : []v1.Container {}}
201
+ for _ , imageString := range o .Images {
202
+ // Retain just the image name
203
+ imageSplit := strings .Split (imageString , "/" )
204
+ name := imageSplit [len (imageSplit )- 1 ]
205
+ // Remove any tag or hash
206
+ if strings .Contains (name , ":" ) {
207
+ name = strings .Split (name , ":" )[0 ]
208
+ }
209
+ if strings .Contains (name , "@" ) {
210
+ name = strings .Split (name , "@" )[0 ]
211
+ }
212
+ name = sanitizeAndUniquify (name )
213
+ podSpec .Containers = append (podSpec .Containers , v1.Container {Name : name , Image : imageString })
214
+ }
215
+ return podSpec
216
+ }
217
+
218
+ // sanitizeAndUniquify replaces characters like "." or "_" into "-" to follow DNS1123 rules.
219
+ // Then add random suffix to make it uniquified.
220
+ func sanitizeAndUniquify (name string ) string {
221
+ if strings .ContainsAny (name , "_." ) {
222
+ name = strings .Replace (name , "_" , "-" , - 1 )
223
+ name = strings .Replace (name , "." , "-" , - 1 )
224
+ name = fmt .Sprintf ("%s-%s" , name , utilrand .String (5 ))
225
+ }
226
+ return name
156
227
}
0 commit comments