add new command karmadactl create

Signed-off-by: hulizhe <pompeii.hu@gmail.com>
This commit is contained in:
hulizhe 2024-08-19 17:16:12 +08:00
parent c7571227f5
commit a6aa08e6b3
25 changed files with 6451 additions and 0 deletions

View File

@ -0,0 +1,53 @@
/*
Copyright 2024 The Karmada 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 create
import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
kubectlcreate "k8s.io/kubectl/pkg/cmd/create"
"k8s.io/kubectl/pkg/util/templates"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
)
var (
createLong = templates.LongDesc(`
Create a resource from a file or from stdin.
JSON and YAML formats are accepted.`)
createExample = templates.Examples(`
# Create a pod using the data in pod.json
%[1]s create -f ./pod.json
# Create a pod based on the JSON passed into stdin
cat pod.json | %[1]s create -f -
# Edit the data in registry.yaml in JSON then create the resource using the edited data
%[1]s create -f registry.yaml --edit -o json`)
)
// NewCmdCreate returns new initialized instance of create sub command
func NewCmdCreate(f util.Factory, parentCommnd string, ioStreams genericiooptions.IOStreams) *cobra.Command {
cmd := kubectlcreate.NewCmdCreate(f, ioStreams)
cmd.Long = fmt.Sprintf(createLong, parentCommnd)
cmd.Example = fmt.Sprintf(createExample, parentCommnd)
return cmd
}

View File

@ -32,6 +32,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/apply"
"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit"
"github.com/karmada-io/karmada/pkg/karmadactl/cordon"
"github.com/karmada-io/karmada/pkg/karmadactl/create"
"github.com/karmada-io/karmada/pkg/karmadactl/deinit"
"github.com/karmada-io/karmada/pkg/karmadactl/describe"
"github.com/karmada-io/karmada/pkg/karmadactl/exec"
@ -87,6 +88,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
Message: "Basic Commands:",
Commands: []*cobra.Command{
get.NewCmdGet(f, parentCommand, ioStreams),
create.NewCmdCreate(f, parentCommand, ioStreams),
},
},
{

472
vendor/k8s.io/kubectl/pkg/cmd/create/create.go generated vendored Normal file
View File

@ -0,0 +1,472 @@
/*
Copyright 2014 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 create
import (
"context"
"fmt"
"io"
"net/url"
"runtime"
"strings"
"github.com/spf13/cobra"
"k8s.io/klog/v2"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/dynamic"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/cmd/util/editor"
"k8s.io/kubectl/pkg/generate"
"k8s.io/kubectl/pkg/rawhttp"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
// CreateOptions is the commandline options for 'create' sub command
type CreateOptions struct {
PrintFlags *genericclioptions.PrintFlags
RecordFlags *genericclioptions.RecordFlags
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
fieldManager string
FilenameOptions resource.FilenameOptions
Selector string
EditBeforeCreate bool
Raw string
Recorder genericclioptions.Recorder
PrintObj func(obj kruntime.Object) error
genericiooptions.IOStreams
}
var (
createLong = templates.LongDesc(i18n.T(`
Create a resource from a file or from stdin.
JSON and YAML formats are accepted.`))
createExample = templates.Examples(i18n.T(`
# Create a pod using the data in pod.json
kubectl create -f ./pod.json
# Create a pod based on the JSON passed into stdin
cat pod.json | kubectl create -f -
# Edit the data in registry.yaml in JSON then create the resource using the edited data
kubectl create -f registry.yaml --edit -o json`))
)
// NewCreateOptions returns an initialized CreateOptions instance
func NewCreateOptions(ioStreams genericiooptions.IOStreams) *CreateOptions {
return &CreateOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
}
}
// NewCmdCreate returns new initialized instance of create sub command
func NewCmdCreate(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewCreateOptions(ioStreams)
cmd := &cobra.Command{
Use: "create -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a resource from a file or from stdin"),
Long: createLong,
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
ioStreams.ErrOut.Write([]byte("Error: must specify one of -f and -k\n\n"))
defaultRunFunc := cmdutil.DefaultSubCommandRun(ioStreams.ErrOut)
defaultRunFunc(cmd, args)
return
}
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunCreate(f, cmd))
},
}
// bind flag structs
o.RecordFlags.AddFlags(cmd)
usage := "to use to create the resource"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddValidateFlags(cmd)
cmd.Flags().BoolVar(&o.EditBeforeCreate, "edit", o.EditBeforeCreate, "Edit the API resource before creating")
cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
"Only relevant if --edit=true. Defaults to the line ending native to your platform.")
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector)
cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to POST to the server. Uses the transport specified by the kubeconfig file.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-create")
o.PrintFlags.AddFlags(cmd)
// create subcommands
cmd.AddCommand(NewCmdCreateNamespace(f, ioStreams))
cmd.AddCommand(NewCmdCreateQuota(f, ioStreams))
cmd.AddCommand(NewCmdCreateSecret(f, ioStreams))
cmd.AddCommand(NewCmdCreateConfigMap(f, ioStreams))
cmd.AddCommand(NewCmdCreateServiceAccount(f, ioStreams))
cmd.AddCommand(NewCmdCreateService(f, ioStreams))
cmd.AddCommand(NewCmdCreateDeployment(f, ioStreams))
cmd.AddCommand(NewCmdCreateClusterRole(f, ioStreams))
cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, ioStreams))
cmd.AddCommand(NewCmdCreateRole(f, ioStreams))
cmd.AddCommand(NewCmdCreateRoleBinding(f, ioStreams))
cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, ioStreams))
cmd.AddCommand(NewCmdCreatePriorityClass(f, ioStreams))
cmd.AddCommand(NewCmdCreateJob(f, ioStreams))
cmd.AddCommand(NewCmdCreateCronJob(f, ioStreams))
cmd.AddCommand(NewCmdCreateIngress(f, ioStreams))
cmd.AddCommand(NewCmdCreateToken(f, ioStreams))
return cmd
}
// Validate makes sure there is no discrepency in command options
func (o *CreateOptions) Validate() error {
if len(o.Raw) > 0 {
if o.EditBeforeCreate {
return fmt.Errorf("--raw and --edit are mutually exclusive")
}
if len(o.FilenameOptions.Filenames) != 1 {
return fmt.Errorf("--raw can only use a single local file or stdin")
}
if strings.Index(o.FilenameOptions.Filenames[0], "http://") == 0 || strings.Index(o.FilenameOptions.Filenames[0], "https://") == 0 {
return fmt.Errorf("--raw cannot read from a url")
}
if o.FilenameOptions.Recursive {
return fmt.Errorf("--raw and --recursive are mutually exclusive")
}
if len(o.Selector) > 0 {
return fmt.Errorf("--raw and --selector (-l) are mutually exclusive")
}
if o.PrintFlags.OutputFormat != nil && len(*o.PrintFlags.OutputFormat) > 0 {
return fmt.Errorf("--raw and --output are mutually exclusive")
}
if _, err := url.ParseRequestURI(o.Raw); err != nil {
return fmt.Errorf("--raw must be a valid URL path: %v", err)
}
}
return nil
}
// Complete completes all the required options
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
}
var err error
o.RecordFlags.Complete(cmd)
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj kruntime.Object) error {
return printer.PrintObj(obj, o.Out)
}
return nil
}
// RunCreate performs the creation
func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
// raw only makes sense for a single file resource multiple objects aren't likely to do what you want.
// the validator enforces this, so
if len(o.Raw) > 0 {
restClient, err := f.RESTClient()
if err != nil {
return err
}
return rawhttp.RawPost(restClient, o.IOStreams, o.Raw, o.FilenameOptions.Filenames[0])
}
if o.EditBeforeCreate {
return RunEditOnCreate(f, o.PrintFlags, o.RecordFlags, o.IOStreams, cmd, &o.FilenameOptions, o.fieldManager)
}
schema, err := f.Validator(o.ValidationDirective)
if err != nil {
return err
}
cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
r := f.NewBuilder().
Unstructured().
Schema(schema).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
LabelSelectorParam(o.Selector).
Flatten().
Do()
err = r.Err()
if err != nil {
return err
}
count := 0
err = r.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info.Object, scheme.DefaultJSONEncoder()); err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err)
}
if err := o.Recorder.Record(info.Object); err != nil {
klog.V(4).Infof("error recording current command: %v", err)
}
if o.DryRunStrategy != cmdutil.DryRunClient {
obj, err := resource.
NewHelper(info.Client, info.Mapping).
DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
WithFieldManager(o.fieldManager).
WithFieldValidation(o.ValidationDirective).
Create(info.Namespace, true, info.Object)
if err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err)
}
info.Refresh(obj, true)
}
count++
return o.PrintObj(info.Object)
})
if err != nil {
return err
}
if count == 0 {
return fmt.Errorf("no objects passed to create")
}
return nil
}
// RunEditOnCreate performs edit on creation
func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags, recordFlags *genericclioptions.RecordFlags, ioStreams genericiooptions.IOStreams, cmd *cobra.Command, options *resource.FilenameOptions, fieldManager string) error {
editOptions := editor.NewEditOptions(editor.EditBeforeCreateMode, ioStreams)
editOptions.FilenameOptions = *options
validationDirective, err := cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
editOptions.ValidateOptions = cmdutil.ValidateOptions{
ValidationDirective: string(validationDirective),
}
editOptions.PrintFlags = printFlags
editOptions.ApplyAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
editOptions.RecordFlags = recordFlags
editOptions.FieldManager = "kubectl-create"
err = editOptions.Complete(f, []string{}, cmd)
if err != nil {
return err
}
return editOptions.Run()
}
// NameFromCommandArgs is a utility function for commands that assume the first argument is a resource name
func NameFromCommandArgs(cmd *cobra.Command, args []string) (string, error) {
argsLen := cmd.ArgsLenAtDash()
// ArgsLenAtDash returns -1 when -- was not specified
if argsLen == -1 {
argsLen = len(args)
}
if argsLen != 1 {
return "", cmdutil.UsageErrorf(cmd, "exactly one NAME is required, got %d", argsLen)
}
return args[0], nil
}
// CreateSubcommandOptions is an options struct to support create subcommands
type CreateSubcommandOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
// Name of resource being created
Name string
// StructuredGenerator is the resource generator for the object being created
StructuredGenerator generate.StructuredGenerator
DryRunStrategy cmdutil.DryRunStrategy
CreateAnnotation bool
FieldManager string
ValidationDirective string
Namespace string
EnforceNamespace bool
Mapper meta.RESTMapper
DynamicClient dynamic.Interface
PrintObj printers.ResourcePrinterFunc
genericiooptions.IOStreams
}
// NewCreateSubcommandOptions returns initialized CreateSubcommandOptions
func NewCreateSubcommandOptions(ioStreams genericiooptions.IOStreams) *CreateSubcommandOptions {
return &CreateSubcommandOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// Complete completes all the required options
func (o *CreateSubcommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, generator generate.StructuredGenerator) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
o.StructuredGenerator = generator
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
o.PrintObj = func(obj kruntime.Object, out io.Writer) error {
return printer.PrintObj(obj, out)
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.DynamicClient, err = f.DynamicClient()
if err != nil {
return err
}
o.Mapper, err = f.ToRESTMapper()
if err != nil {
return err
}
return nil
}
// Run executes a create subcommand using the specified options
func (o *CreateSubcommandOptions) Run() error {
obj, err := o.StructuredGenerator.StructuredGenerate()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, obj, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
// create subcommands have compiled knowledge of things they create, so type them directly
gvks, _, err := scheme.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
gvk := gvks[0]
mapping, err := o.Mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
if err != nil {
return err
}
asUnstructured := &unstructured.Unstructured{}
if err := scheme.Scheme.Convert(obj, asUnstructured, nil); err != nil {
return err
}
if mapping.Scope.Name() == meta.RESTScopeNameRoot {
o.Namespace = ""
}
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
actualObject, err := o.DynamicClient.Resource(mapping.Resource).Namespace(o.Namespace).Create(context.TODO(), asUnstructured, createOptions)
if err != nil {
return err
}
// ensure we pass a versioned object to the printer
obj = actualObject
} else {
if meta, err := meta.Accessor(obj); err == nil && o.EnforceNamespace {
meta.SetNamespace(o.Namespace)
}
}
return o.PrintObj(obj, o.Out)
}

View File

@ -0,0 +1,227 @@
/*
Copyright 2017 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 create
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericiooptions"
cliflag "k8s.io/component-base/cli/flag"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
clusterRoleLong = templates.LongDesc(i18n.T(`
Create a cluster role.`))
clusterRoleExample = templates.Examples(i18n.T(`
# Create a cluster role named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
# Create a cluster role named "pod-reader" with ResourceName specified
kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
# Create a cluster role named "foo" with API Group specified
kubectl create clusterrole foo --verb=get,list,watch --resource=rs.apps
# Create a cluster role named "foo" with SubResource specified
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
# Create a cluster role name "foo" with NonResourceURL specified
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
# Create a cluster role name "monitoring" with AggregationRule specified
kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"`))
// Valid nonResource verb list for validation.
validNonResourceVerbs = []string{"*", "get", "post", "put", "delete", "patch", "head", "options"}
)
// CreateClusterRoleOptions is returned by NewCmdCreateClusterRole
type CreateClusterRoleOptions struct {
*CreateRoleOptions
NonResourceURLs []string
AggregationRule map[string]string
FieldManager string
}
// NewCmdCreateClusterRole initializes and returns new ClusterRoles command
func NewCmdCreateClusterRole(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
c := &CreateClusterRoleOptions{
CreateRoleOptions: NewCreateRoleOptions(ioStreams),
AggregationRule: map[string]string{},
}
cmd := &cobra.Command{
Use: "clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a cluster role"),
Long: clusterRoleLong,
Example: clusterRoleExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(c.Complete(f, cmd, args))
cmdutil.CheckErr(c.Validate())
cmdutil.CheckErr(c.RunCreateRole())
},
}
c.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringSliceVar(&c.Verbs, "verb", c.Verbs, "Verb that applies to the resources contained in the rule")
cmd.Flags().StringSliceVar(&c.NonResourceURLs, "non-resource-url", c.NonResourceURLs, "A partial url that user should have access to.")
cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
cmd.Flags().StringArrayVar(&c.ResourceNames, "resource-name", c.ResourceNames, "Resource in the white list that the rule applies to, repeat this flag for multiple items")
cmd.Flags().Var(cliflag.NewMapStringString(&c.AggregationRule), "aggregation-rule", "An aggregation label selector for combining ClusterRoles.")
cmdutil.AddFieldManagerFlagVar(cmd, &c.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (c *CreateClusterRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
// Remove duplicate nonResourceURLs
nonResourceURLs := []string{}
for _, n := range c.NonResourceURLs {
if !arrayContains(nonResourceURLs, n) {
nonResourceURLs = append(nonResourceURLs, n)
}
}
c.NonResourceURLs = nonResourceURLs
return c.CreateRoleOptions.Complete(f, cmd, args)
}
// Validate makes sure there is no discrepency in CreateClusterRoleOptions
func (c *CreateClusterRoleOptions) Validate() error {
if c.Name == "" {
return fmt.Errorf("name must be specified")
}
if len(c.AggregationRule) > 0 {
if len(c.NonResourceURLs) > 0 || len(c.Verbs) > 0 || len(c.Resources) > 0 || len(c.ResourceNames) > 0 {
return fmt.Errorf("aggregation rule must be specified without nonResourceURLs, verbs, resources or resourceNames")
}
return nil
}
// validate verbs.
if len(c.Verbs) == 0 {
return fmt.Errorf("at least one verb must be specified")
}
if len(c.Resources) == 0 && len(c.NonResourceURLs) == 0 {
return fmt.Errorf("one of resource or nonResourceURL must be specified")
}
// validate resources
if len(c.Resources) > 0 {
for _, v := range c.Verbs {
if !arrayContains(validResourceVerbs, v) {
fmt.Fprintf(c.ErrOut, "Warning: '%s' is not a standard resource verb\n", v)
}
}
if err := c.validateResource(); err != nil {
return err
}
}
//validate non-resource-url
if len(c.NonResourceURLs) > 0 {
for _, v := range c.Verbs {
if !arrayContains(validNonResourceVerbs, v) {
return fmt.Errorf("invalid verb: '%s' for nonResourceURL", v)
}
}
for _, nonResourceURL := range c.NonResourceURLs {
if nonResourceURL == "*" {
continue
}
if nonResourceURL == "" || !strings.HasPrefix(nonResourceURL, "/") {
return fmt.Errorf("nonResourceURL should start with /")
}
if strings.ContainsRune(nonResourceURL[:len(nonResourceURL)-1], '*') {
return fmt.Errorf("nonResourceURL only supports wildcard matches when '*' is at the end")
}
}
}
return nil
}
// RunCreateRole creates a new clusterRole
func (c *CreateClusterRoleOptions) RunCreateRole() error {
clusterRole := &rbacv1.ClusterRole{
// this is ok because we know exactly how we want to be serialized
TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRole"},
}
clusterRole.Name = c.Name
var err error
if len(c.AggregationRule) == 0 {
rules, err := generateResourcePolicyRules(c.Mapper, c.Verbs, c.Resources, c.ResourceNames, c.NonResourceURLs)
if err != nil {
return err
}
clusterRole.Rules = rules
} else {
clusterRole.AggregationRule = &rbacv1.AggregationRule{
ClusterRoleSelectors: []metav1.LabelSelector{
{
MatchLabels: c.AggregationRule,
},
},
}
}
if err := util.CreateOrUpdateAnnotation(c.CreateAnnotation, clusterRole, scheme.DefaultJSONEncoder()); err != nil {
return err
}
// Create ClusterRole.
if c.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if c.FieldManager != "" {
createOptions.FieldManager = c.FieldManager
}
createOptions.FieldValidation = c.ValidationDirective
if c.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
clusterRole, err = c.Client.ClusterRoles().Create(context.TODO(), clusterRole, createOptions)
if err != nil {
return err
}
}
return c.PrintObj(clusterRole)
}

View File

@ -0,0 +1,228 @@
/*
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 create
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
rbacclientv1 "k8s.io/client-go/kubernetes/typed/rbac/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
clusterRoleBindingLong = templates.LongDesc(i18n.T(`
Create a cluster role binding for a particular cluster role.`))
clusterRoleBindingExample = templates.Examples(i18n.T(`
# Create a cluster role binding for user1, user2, and group1 using the cluster-admin cluster role
kubectl create clusterrolebinding cluster-admin --clusterrole=cluster-admin --user=user1 --user=user2 --group=group1`))
)
// ClusterRoleBindingOptions is returned by NewCmdCreateClusterRoleBinding
type ClusterRoleBindingOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
ClusterRole string
Users []string
Groups []string
ServiceAccounts []string
FieldManager string
CreateAnnotation bool
Client rbacclientv1.RbacV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewClusterRoleBindingOptions creates a new *ClusterRoleBindingOptions with sane defaults
func NewClusterRoleBindingOptions(ioStreams genericiooptions.IOStreams) *ClusterRoleBindingOptions {
return &ClusterRoleBindingOptions{
Users: []string{},
Groups: []string{},
ServiceAccounts: []string{},
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateClusterRoleBinding returns an initialized command instance of ClusterRoleBinding
func NewCmdCreateClusterRoleBinding(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewClusterRoleBindingOptions(ioStreams)
cmd := &cobra.Command{
Use: "clusterrolebinding NAME --clusterrole=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a cluster role binding for a particular cluster role"),
Long: clusterRoleBindingLong,
Example: clusterRoleBindingExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.ClusterRole, "clusterrole", "", i18n.T("ClusterRole this ClusterRoleBinding should reference"))
cmd.MarkFlagRequired("clusterrole")
cmd.Flags().StringArrayVar(&o.Users, "user", o.Users, "Usernames to bind to the clusterrole. The flag can be repeated to add multiple users.")
cmd.Flags().StringArrayVar(&o.Groups, "group", o.Groups, "Groups to bind to the clusterrole. The flag can be repeated to add multiple groups.")
cmd.Flags().StringArrayVar(&o.ServiceAccounts, "serviceaccount", o.ServiceAccounts, "Service accounts to bind to the clusterrole, in the format <namespace>:<name>. The flag can be repeated to add multiple service accounts.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
// Completion for relevant flags
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"clusterrole",
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completion.CompGetResource(f, "clusterrole", toComplete), cobra.ShellCompDirectiveNoFileComp
}))
return cmd
}
// Complete completes all the required options
func (o *ClusterRoleBindingOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
cs, err := f.KubernetesClientSet()
if err != nil {
return err
}
o.Client = cs.RbacV1()
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Run calls the CreateSubcommandOptions.Run in ClusterRoleBindingOptions instance
func (o *ClusterRoleBindingOptions) Run() error {
clusterRoleBinding, err := o.createClusterRoleBinding()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, clusterRoleBinding, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
clusterRoleBinding, err = o.Client.ClusterRoleBindings().Create(context.TODO(), clusterRoleBinding, createOptions)
if err != nil {
return fmt.Errorf("failed to create clusterrolebinding: %v", err)
}
}
return o.PrintObj(clusterRoleBinding)
}
func (o *ClusterRoleBindingOptions) createClusterRoleBinding() (*rbacv1.ClusterRoleBinding, error) {
clusterRoleBinding := &rbacv1.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRoleBinding"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
},
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "ClusterRole",
Name: o.ClusterRole,
},
}
for _, user := range o.Users {
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1.Subject{
Kind: rbacv1.UserKind,
APIGroup: rbacv1.GroupName,
Name: user,
})
}
for _, group := range o.Groups {
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1.Subject{
Kind: rbacv1.GroupKind,
APIGroup: rbacv1.GroupName,
Name: group,
})
}
for _, sa := range o.ServiceAccounts {
tokens := strings.Split(sa, ":")
if len(tokens) != 2 || tokens[0] == "" || tokens[1] == "" {
return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
}
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1.Subject{
Kind: rbacv1.ServiceAccountKind,
APIGroup: "",
Namespace: tokens[0],
Name: tokens[1],
})
}
return clusterRoleBinding, nil
}

View File

@ -0,0 +1,414 @@
/*
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 create
import (
"context"
"fmt"
"os"
"path"
"strings"
"unicode/utf8"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/hash"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
configMapLong = templates.LongDesc(i18n.T(`
Create a config map based on a file, directory, or specified literal value.
A single config map may package one or more key/value pairs.
When creating a config map based on a file, the key will default to the basename of the file, and the value will
default to the file content. If the basename is an invalid key, you may specify an alternate key.
When creating a config map based on a directory, each file whose basename is a valid key in the directory will be
packaged into the config map. Any directory entries except regular files are ignored (e.g. subdirectories,
symlinks, devices, pipes, etc).`))
configMapExample = templates.Examples(i18n.T(`
# Create a new config map named my-config based on folder bar
kubectl create configmap my-config --from-file=path/to/bar
# Create a new config map named my-config with specified keys instead of file basenames on disk
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
# Create a new config map named my-config with key1=config1 and key2=config2
kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
# Create a new config map named my-config from the key=value pairs in the file
kubectl create configmap my-config --from-file=path/to/bar
# Create a new config map named my-config from an env file
kubectl create configmap my-config --from-env-file=path/to/foo.env --from-env-file=path/to/bar.env`))
)
// ConfigMapOptions holds properties for create configmap sub-command
type ConfigMapOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Name of configMap (required)
Name string
// Type of configMap (optional)
Type string
// FileSources to derive the configMap from (optional)
FileSources []string
// LiteralSources to derive the configMap from (optional)
LiteralSources []string
// EnvFileSources to derive the configMap from (optional)
EnvFileSources []string
// AppendHash; if true, derive a hash from the ConfigMap and append it to the name
AppendHash bool
FieldManager string
CreateAnnotation bool
Namespace string
EnforceNamespace bool
Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewConfigMapOptions creates a new *ConfigMapOptions with default value
func NewConfigMapOptions(ioStreams genericiooptions.IOStreams) *ConfigMapOptions {
return &ConfigMapOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateConfigMap creates the `create configmap` Cobra command
func NewCmdCreateConfigMap(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewConfigMapOptions(ioStreams)
cmd := &cobra.Command{
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Aliases: []string{"cm"},
Short: i18n.T("Create a config map from a local file, directory or literal value"),
Long: configMapLong,
Example: configMapExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringSliceVar(&o.FileSources, "from-file", o.FileSources, "Key file can be specified using its file path, in which case file basename will be used as configmap key, or optionally with a key and file path, in which case the given key will be used. Specifying a directory will iterate each named file in the directory whose basename is a valid configmap key.")
cmd.Flags().StringArrayVar(&o.LiteralSources, "from-literal", o.LiteralSources, "Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
cmd.Flags().StringSliceVar(&o.EnvFileSources, "from-env-file", o.EnvFileSources, "Specify the path to a file to read lines of key=val pairs to create a configmap.")
cmd.Flags().BoolVar(&o.AppendHash, "append-hash", o.AppendHash, "Append a hash of the configmap to its name.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete loads data from the command line environment
func (o *ConfigMapOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = corev1client.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks if ConfigMapOptions has sufficient value to run
func (o *ConfigMapOptions) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(o.EnvFileSources) > 0 && (len(o.FileSources) > 0 || len(o.LiteralSources) > 0) {
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
}
return nil
}
// Run calls createConfigMap and filled in value for configMap object
func (o *ConfigMapOptions) Run() error {
configMap, err := o.createConfigMap()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, configMap, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
configMap, err = o.Client.ConfigMaps(o.Namespace).Create(context.TODO(), configMap, createOptions)
if err != nil {
return fmt.Errorf("failed to create configmap: %v", err)
}
}
return o.PrintObj(configMap)
}
// createConfigMap fills in key value pair from the information given in
// ConfigMapOptions into *corev1.ConfigMap
func (o *ConfigMapOptions) createConfigMap() (*corev1.ConfigMap, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
configMap := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Namespace: namespace,
},
}
configMap.Name = o.Name
configMap.Data = map[string]string{}
configMap.BinaryData = map[string][]byte{}
if len(o.FileSources) > 0 {
if err := handleConfigMapFromFileSources(configMap, o.FileSources); err != nil {
return nil, err
}
}
if len(o.LiteralSources) > 0 {
if err := handleConfigMapFromLiteralSources(configMap, o.LiteralSources); err != nil {
return nil, err
}
}
if len(o.EnvFileSources) > 0 {
if err := handleConfigMapFromEnvFileSources(configMap, o.EnvFileSources); err != nil {
return nil, err
}
}
if o.AppendHash {
hash, err := hash.ConfigMapHash(configMap)
if err != nil {
return nil, err
}
configMap.Name = fmt.Sprintf("%s-%s", configMap.Name, hash)
}
return configMap, nil
}
// handleConfigMapFromLiteralSources adds the specified literal source
// information into the provided configMap.
func handleConfigMapFromLiteralSources(configMap *corev1.ConfigMap, literalSources []string) error {
for _, literalSource := range literalSources {
keyName, value, err := util.ParseLiteralSource(literalSource)
if err != nil {
return err
}
err = addKeyFromLiteralToConfigMap(configMap, keyName, value)
if err != nil {
return err
}
}
return nil
}
// handleConfigMapFromFileSources adds the specified file source information
// into the provided configMap
func handleConfigMapFromFileSources(configMap *corev1.ConfigMap, fileSources []string) error {
for _, fileSource := range fileSources {
keyName, filePath, err := util.ParseFileSource(fileSource)
if err != nil {
return err
}
info, err := os.Stat(filePath)
if err != nil {
switch err := err.(type) {
case *os.PathError:
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
default:
return fmt.Errorf("error reading %s: %v", filePath, err)
}
}
if info.IsDir() {
if strings.Contains(fileSource, "=") {
return fmt.Errorf("cannot give a key name for a directory path")
}
fileList, err := os.ReadDir(filePath)
if err != nil {
return fmt.Errorf("error listing files in %s: %v", filePath, err)
}
for _, item := range fileList {
itemPath := path.Join(filePath, item.Name())
if item.Type().IsRegular() {
keyName = item.Name()
err = addKeyFromFileToConfigMap(configMap, keyName, itemPath)
if err != nil {
return err
}
}
}
} else {
if err := addKeyFromFileToConfigMap(configMap, keyName, filePath); err != nil {
return err
}
}
}
return nil
}
// handleConfigMapFromEnvFileSources adds the specified env file source information
// into the provided configMap
func handleConfigMapFromEnvFileSources(configMap *corev1.ConfigMap, envFileSources []string) error {
for _, envFileSource := range envFileSources {
info, err := os.Stat(envFileSource)
if err != nil {
switch err := err.(type) {
case *os.PathError:
return fmt.Errorf("error reading %s: %v", envFileSource, err.Err)
default:
return fmt.Errorf("error reading %s: %v", envFileSource, err)
}
}
if info.IsDir() {
return fmt.Errorf("env config file cannot be a directory")
}
err = cmdutil.AddFromEnvFile(envFileSource, func(key, value string) error {
return addKeyFromLiteralToConfigMap(configMap, key, value)
})
if err != nil {
return err
}
}
return nil
}
// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating
// the value with the content of the given file path, or returns an error.
func addKeyFromFileToConfigMap(configMap *corev1.ConfigMap, keyName, filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return err
}
if utf8.Valid(data) {
return addKeyFromLiteralToConfigMap(configMap, keyName, string(data))
}
err = validateNewConfigMap(configMap, keyName)
if err != nil {
return err
}
configMap.BinaryData[keyName] = data
return nil
}
// addKeyFromLiteralToConfigMap adds the given key and data to the given config map,
// returning an error if the key is not valid or if the key already exists.
func addKeyFromLiteralToConfigMap(configMap *corev1.ConfigMap, keyName, data string) error {
err := validateNewConfigMap(configMap, keyName)
if err != nil {
return err
}
configMap.Data[keyName] = data
return nil
}
// validateNewConfigMap checks whether the keyname is valid
// Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys.
func validateNewConfigMap(configMap *corev1.ConfigMap, keyName string) error {
if errs := validation.IsConfigMapKey(keyName); len(errs) > 0 {
return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ","))
}
if _, exists := configMap.Data[keyName]; exists {
return fmt.Errorf("cannot add key %q, another key by that name already exists in Data for ConfigMap %q", keyName, configMap.Name)
}
if _, exists := configMap.BinaryData[keyName]; exists {
return fmt.Errorf("cannot add key %q, another key by that name already exists in BinaryData for ConfigMap %q", keyName, configMap.Name)
}
return nil
}

226
vendor/k8s.io/kubectl/pkg/cmd/create/create_cronjob.go generated vendored Normal file
View File

@ -0,0 +1,226 @@
/*
Copyright 2018 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 create
import (
"context"
"fmt"
"github.com/spf13/cobra"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/cli-runtime/pkg/resource"
batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
cronjobLong = templates.LongDesc(i18n.T(`
Create a cron job with the specified name.`))
cronjobExample = templates.Examples(`
# Create a cron job
kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *"
# Create a cron job with a command
kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *" -- date`)
)
// CreateCronJobOptions is returned by NewCreateCronJobOptions
type CreateCronJobOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
Image string
Schedule string
Command []string
Restart string
Namespace string
EnforceNamespace bool
Client batchv1client.BatchV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
Builder *resource.Builder
FieldManager string
CreateAnnotation bool
genericiooptions.IOStreams
}
// NewCreateCronJobOptions returns an initialized CreateCronJobOptions instance
func NewCreateCronJobOptions(ioStreams genericiooptions.IOStreams) *CreateCronJobOptions {
return &CreateCronJobOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateCronJob is a command to create CronJobs.
func NewCmdCreateCronJob(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewCreateCronJobOptions(ioStreams)
cmd := &cobra.Command{
Use: "cronjob NAME --image=image --schedule='0/5 * * * ?' -- [COMMAND] [args...]",
DisableFlagsInUseLine: false,
Aliases: []string{"cj"},
Short: i18n.T("Create a cron job with the specified name"),
Long: cronjobLong,
Example: cronjobExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
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.MarkFlagRequired("schedule")
cmd.Flags().StringVar(&o.Restart, "restart", o.Restart, "job's restart policy. supported values: OnFailure, Never")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *CreateCronJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
if len(args) > 1 {
o.Command = args[1:]
}
if len(o.Restart) == 0 {
o.Restart = "OnFailure"
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = batchv1client.NewForConfig(clientConfig)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.Builder = f.NewBuilder()
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Run performs the execution of 'create cronjob' sub command
func (o *CreateCronJobOptions) Run() error {
cronJob := o.createCronJob()
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, cronJob, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
cronJob, err = o.Client.CronJobs(o.Namespace).Create(context.TODO(), cronJob, createOptions)
if err != nil {
return fmt.Errorf("failed to create cronjob: %v", err)
}
}
return o.PrintObj(cronJob)
}
func (o *CreateCronJobOptions) createCronJob() *batchv1.CronJob {
cronjob := &batchv1.CronJob{
TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "CronJob"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
},
Spec: batchv1.CronJobSpec{
Schedule: o.Schedule,
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
},
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: o.Name,
Image: o.Image,
Command: o.Command,
},
},
RestartPolicy: corev1.RestartPolicy(o.Restart),
},
},
},
},
},
}
if o.EnforceNamespace {
cronjob.Namespace = o.Namespace
}
return cronjob
}

View File

@ -0,0 +1,278 @@
/*
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 create
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
utilrand "k8s.io/apimachinery/pkg/util/rand"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
deploymentLong = templates.LongDesc(i18n.T(`
Create a deployment with the specified name.`))
deploymentExample = templates.Examples(i18n.T(`
# Create a deployment named my-dep that runs the busybox image
kubectl create deployment my-dep --image=busybox
# Create a deployment with a command
kubectl create deployment my-dep --image=busybox -- date
# Create a deployment named my-dep that runs the nginx image with 3 replicas
kubectl create deployment my-dep --image=nginx --replicas=3
# Create a deployment named my-dep that runs the busybox image and expose port 5701
kubectl create deployment my-dep --image=busybox --port=5701
# Create a deployment named my-dep that runs multiple containers
kubectl create deployment my-dep --image=busybox:latest --image=ubuntu:latest --image=nginx`))
)
// CreateDeploymentOptions is returned by NewCmdCreateDeployment
type CreateDeploymentOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
Images []string
Port int32
Replicas int32
Command []string
Namespace string
EnforceNamespace bool
FieldManager string
CreateAnnotation bool
Client appsv1client.AppsV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewCreateDeploymentOptions returns an initialized CreateDeploymentOptions instance
func NewCreateDeploymentOptions(ioStreams genericiooptions.IOStreams) *CreateDeploymentOptions {
return &CreateDeploymentOptions{
Port: -1,
Replicas: 1,
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
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 genericiooptions.IOStreams) *cobra.Command {
o := NewCreateDeploymentOptions(ioStreams)
cmd := &cobra.Command{
Use: "deployment NAME --image=image -- [COMMAND] [args...]",
DisableFlagsInUseLine: true,
Aliases: []string{"deploy"},
Short: i18n.T("Create a deployment with the specified name"),
Long: deploymentLong,
Example: deploymentExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringSliceVar(&o.Images, "image", o.Images, "Image names to run. A deployment can have multiple images set for multi-container pod.")
cmd.MarkFlagRequired("image")
cmd.Flags().Int32Var(&o.Port, "port", o.Port, "The containerPort that this deployment exposes.")
cmd.Flags().Int32VarP(&o.Replicas, "replicas", "r", o.Replicas, "Number of replicas to create. Default is 1.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the options
func (o *CreateDeploymentOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
if len(args) > 1 {
o.Command = args[1:]
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = appsv1client.NewForConfig(clientConfig)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate makes sure there is no discrepency in provided option values
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
func (o *CreateDeploymentOptions) Run() error {
deploy := o.createDeployment()
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, deploy, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
deploy, err = o.Client.Deployments(o.Namespace).Create(context.TODO(), deploy, createOptions)
if err != nil {
return fmt.Errorf("failed to create deployment: %v", err)
}
}
return o.PrintObj(deploy)
}
func (o *CreateDeploymentOptions) createDeployment() *appsv1.Deployment {
labels := map[string]string{"app": o.Name}
selector := metav1.LabelSelector{MatchLabels: labels}
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
deploy := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{APIVersion: appsv1.SchemeGroupVersion.String(), Kind: "Deployment"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Labels: labels,
Namespace: namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: &o.Replicas,
Selector: &selector,
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: o.buildPodSpec(),
},
},
}
if o.Port >= 0 && len(deploy.Spec.Template.Spec.Containers) > 0 {
deploy.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{{ContainerPort: o.Port}}
}
return deploy
}
// 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.
func (o *CreateDeploymentOptions) buildPodSpec() corev1.PodSpec {
podSpec := corev1.PodSpec{Containers: []corev1.Container{}}
for _, imageString := range o.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]
}
if strings.Contains(name, "@") {
name = strings.Split(name, "@")[0]
}
name = sanitizeAndUniquify(name)
podSpec.Containers = append(podSpec.Containers, corev1.Container{
Name: name,
Image: imageString,
Command: o.Command,
})
}
return podSpec
}
// sanitizeAndUniquify replaces characters like "." or "_" into "-" to follow DNS1123 rules.
// Then add random suffix to make it uniquified.
func sanitizeAndUniquify(name string) string {
if strings.ContainsAny(name, "_.") {
name = strings.Replace(name, "_", "-", -1)
name = strings.Replace(name, ".", "-", -1)
name = fmt.Sprintf("%s-%s", name, utilrand.String(5))
}
return name
}

458
vendor/k8s.io/kubectl/pkg/cmd/create/create_ingress.go generated vendored Normal file
View File

@ -0,0 +1,458 @@
/*
Copyright 2020 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 create
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/spf13/cobra"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
networkingv1client "k8s.io/client-go/kubernetes/typed/networking/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
// Explaining the Regex below:
// ^(?P<host>[\w\*\-\.]*) -> Indicates the host - 0-N characters of letters, number, underscore, '-', '.' and '*'
// (?P<path>/.*) -> Indicates the path and MUST start with '/' - / + 0-N characters
// Separator from host/path to svcname:svcport -> "="
// (?P<svcname>[\w\-]+) -> Service Name (letters, numbers, '-') -> 1-N characters
// Separator from svcname to svcport -> ":"
// (?P<svcport>[\w\-]+) -> Service Port (letters, numbers, '-') -> 1-N characters
regexHostPathSvc = `^(?P<host>[\w\*\-\.]*)(?P<path>/.*)=(?P<svcname>[\w\-]+):(?P<svcport>[\w\-]+)`
// This Regex is optional -> (....)?
// (?P<istls>tls) -> Verify if the argument after "," is 'tls'
// Optional Separator from tls to the secret name -> "=?"
// (?P<secretname>[\w\-]+)? -> Optional secret name after the separator -> 1-N characters
regexTLS = `(,(?P<istls>tls)=?(?P<secretname>[\w\-]+)?)?`
// The validation Regex is the concatenation of hostPathSvc validation regex
// and the TLS validation regex
ruleRegex = regexHostPathSvc + regexTLS
ingressLong = templates.LongDesc(i18n.T(`
Create an ingress with the specified name.`))
ingressExample = templates.Examples(i18n.T(`
# Create a single ingress called 'simple' that directs requests to foo.com/bar to svc
# svc1:8080 with a TLS secret "my-cert"
kubectl create ingress simple --rule="foo.com/bar=svc1:8080,tls=my-cert"
# Create a catch all ingress of "/path" pointing to service svc:port and Ingress Class as "otheringress"
kubectl create ingress catch-all --class=otheringress --rule="/path=svc:port"
# Create an ingress with two annotations: ingress.annotation1 and ingress.annotations2
kubectl create ingress annotated --class=default --rule="foo.com/bar=svc:port" \
--annotation ingress.annotation1=foo \
--annotation ingress.annotation2=bla
# Create an ingress with the same host and multiple paths
kubectl create ingress multipath --class=default \
--rule="foo.com/=svc:port" \
--rule="foo.com/admin/=svcadmin:portadmin"
# Create an ingress with multiple hosts and the pathType as Prefix
kubectl create ingress ingress1 --class=default \
--rule="foo.com/path*=svc:8080" \
--rule="bar.com/admin*=svc2:http"
# Create an ingress with TLS enabled using the default ingress certificate and different path types
kubectl create ingress ingtls --class=default \
--rule="foo.com/=svc:https,tls" \
--rule="foo.com/path/subpath*=othersvc:8080"
# Create an ingress with TLS enabled using a specific secret and pathType as Prefix
kubectl create ingress ingsecret --class=default \
--rule="foo.com/*=svc:8080,tls=secret1"
# Create an ingress with a default backend
kubectl create ingress ingdefault --class=default \
--default-backend=defaultsvc:http \
--rule="foo.com/*=svc:8080,tls=secret1"
`))
)
// CreateIngressOptions is returned by NewCmdCreateIngress
type CreateIngressOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
IngressClass string
Rules []string
Annotations []string
DefaultBackend string
Namespace string
EnforceNamespace bool
CreateAnnotation bool
Client networkingv1client.NetworkingV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
FieldManager string
genericiooptions.IOStreams
}
// NewCreateIngressOptions creates the CreateIngressOptions to be used later
func NewCreateIngressOptions(ioStreams genericiooptions.IOStreams) *CreateIngressOptions {
return &CreateIngressOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateIngress is a macro command to create a new ingress.
// This command is better known to users as `kubectl create ingress`.
func NewCmdCreateIngress(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewCreateIngressOptions(ioStreams)
cmd := &cobra.Command{
Use: "ingress NAME --rule=host/path=service:port[,tls[=secret]] ",
DisableFlagsInUseLine: true,
Aliases: []string{"ing"},
Short: i18n.T("Create an ingress with the specified name"),
Long: ingressLong,
Example: ingressExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.IngressClass, "class", o.IngressClass, "Ingress Class to be used")
cmd.Flags().StringArrayVar(&o.Rules, "rule", o.Rules, "Rule in format host/path=service:port[,tls=secretname]. Paths containing the leading character '*' are considered pathType=Prefix. tls argument is optional.")
cmd.Flags().StringVar(&o.DefaultBackend, "default-backend", o.DefaultBackend, "Default service for backend, in format of svcname:port")
cmd.Flags().StringArrayVar(&o.Annotations, "annotation", o.Annotations, "Annotation to insert in the ingress object, in the format annotation=value")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the options
func (o *CreateIngressOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = networkingv1client.NewForConfig(clientConfig)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
return err
}
// Validate validates the Ingress object to be created
func (o *CreateIngressOptions) Validate() error {
if len(o.DefaultBackend) == 0 && len(o.Rules) == 0 {
return fmt.Errorf("not enough information provided: every ingress has to either specify a default-backend (which catches all traffic) or a list of rules (which catch specific paths)")
}
rulevalidation, err := regexp.Compile(ruleRegex)
if err != nil {
return fmt.Errorf("failed to compile the regex")
}
for _, rule := range o.Rules {
if match := rulevalidation.MatchString(rule); !match {
return fmt.Errorf("rule %s is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]", rule)
}
}
for _, annotation := range o.Annotations {
if an := strings.SplitN(annotation, "=", 2); len(an) != 2 {
return fmt.Errorf("annotation %s is invalid and should be in format key=[value]", annotation)
}
}
if len(o.DefaultBackend) > 0 && len(strings.Split(o.DefaultBackend, ":")) != 2 {
return fmt.Errorf("default-backend should be in format servicename:serviceport")
}
return nil
}
// Run performs the execution of 'create ingress' sub command
func (o *CreateIngressOptions) Run() error {
ingress := o.createIngress()
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, ingress, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
ingress, err = o.Client.Ingresses(o.Namespace).Create(context.TODO(), ingress, createOptions)
if err != nil {
return fmt.Errorf("failed to create ingress: %v", err)
}
}
return o.PrintObj(ingress)
}
func (o *CreateIngressOptions) createIngress() *networkingv1.Ingress {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
annotations := o.buildAnnotations()
spec := o.buildIngressSpec()
ingress := &networkingv1.Ingress{
TypeMeta: metav1.TypeMeta{APIVersion: networkingv1.SchemeGroupVersion.String(), Kind: "Ingress"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Namespace: namespace,
Annotations: annotations,
},
Spec: spec,
}
return ingress
}
func (o *CreateIngressOptions) buildAnnotations() map[string]string {
var annotations = make(map[string]string)
for _, annotation := range o.Annotations {
an := strings.SplitN(annotation, "=", 2)
annotations[an[0]] = an[1]
}
return annotations
}
// buildIngressSpec builds the .spec from the diverse arguments passed to kubectl
func (o *CreateIngressOptions) buildIngressSpec() networkingv1.IngressSpec {
var ingressSpec networkingv1.IngressSpec
if len(o.IngressClass) > 0 {
ingressSpec.IngressClassName = &o.IngressClass
}
if len(o.DefaultBackend) > 0 {
defaultbackend := buildIngressBackendSvc(o.DefaultBackend)
ingressSpec.DefaultBackend = &defaultbackend
}
ingressSpec.TLS = o.buildTLSRules()
ingressSpec.Rules = o.buildIngressRules()
return ingressSpec
}
func (o *CreateIngressOptions) buildTLSRules() []networkingv1.IngressTLS {
hostAlreadyPresent := make(map[string]struct{})
ingressTLSs := []networkingv1.IngressTLS{}
var secret string
for _, rule := range o.Rules {
tls := strings.Split(rule, ",")
if len(tls) == 2 {
ingressTLS := networkingv1.IngressTLS{}
host := strings.SplitN(rule, "/", 2)[0]
secret = ""
secretName := strings.Split(tls[1], "=")
if len(secretName) > 1 {
secret = secretName[1]
}
idxSecret := getIndexSecret(secret, ingressTLSs)
// We accept the same host into TLS secrets only once
if _, ok := hostAlreadyPresent[host]; !ok {
if idxSecret > -1 {
ingressTLSs[idxSecret].Hosts = append(ingressTLSs[idxSecret].Hosts, host)
hostAlreadyPresent[host] = struct{}{}
continue
}
if host != "" {
ingressTLS.Hosts = append(ingressTLS.Hosts, host)
}
if secret != "" {
ingressTLS.SecretName = secret
}
if len(ingressTLS.SecretName) > 0 || len(ingressTLS.Hosts) > 0 {
ingressTLSs = append(ingressTLSs, ingressTLS)
}
hostAlreadyPresent[host] = struct{}{}
}
}
}
return ingressTLSs
}
// buildIngressRules builds the .spec.rules for an ingress object.
func (o *CreateIngressOptions) buildIngressRules() []networkingv1.IngressRule {
ingressRules := []networkingv1.IngressRule{}
for _, rule := range o.Rules {
removeTLS := strings.Split(rule, ",")[0]
hostSplit := strings.SplitN(removeTLS, "/", 2)
host := hostSplit[0]
ingressPath := buildHTTPIngressPath(hostSplit[1])
ingressRule := networkingv1.IngressRule{}
if host != "" {
ingressRule.Host = host
}
idxHost := getIndexHost(ingressRule.Host, ingressRules)
if idxHost > -1 {
ingressRules[idxHost].IngressRuleValue.HTTP.Paths = append(ingressRules[idxHost].IngressRuleValue.HTTP.Paths, ingressPath)
continue
}
ingressRule.IngressRuleValue = networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
ingressPath,
},
},
}
ingressRules = append(ingressRules, ingressRule)
}
return ingressRules
}
func buildHTTPIngressPath(pathsvc string) networkingv1.HTTPIngressPath {
pathsvcsplit := strings.Split(pathsvc, "=")
path := "/" + pathsvcsplit[0]
service := pathsvcsplit[1]
var pathType networkingv1.PathType
pathType = "Exact"
// If * in the End, turn pathType=Prefix but remove the * from the end
if path[len(path)-1:] == "*" {
pathType = "Prefix"
path = path[0 : len(path)-1]
}
httpIngressPath := networkingv1.HTTPIngressPath{
Path: path,
PathType: &pathType,
Backend: buildIngressBackendSvc(service),
}
return httpIngressPath
}
func buildIngressBackendSvc(service string) networkingv1.IngressBackend {
svcname := strings.Split(service, ":")[0]
svcport := strings.Split(service, ":")[1]
ingressBackend := networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: svcname,
Port: parseServiceBackendPort(svcport),
},
}
return ingressBackend
}
func parseServiceBackendPort(port string) networkingv1.ServiceBackendPort {
var backendPort networkingv1.ServiceBackendPort
portIntOrStr := intstr.Parse(port)
if portIntOrStr.Type == intstr.Int {
backendPort.Number = portIntOrStr.IntVal
}
if portIntOrStr.Type == intstr.String {
backendPort.Name = portIntOrStr.StrVal
}
return backendPort
}
func getIndexHost(host string, rules []networkingv1.IngressRule) int {
for index, v := range rules {
if v.Host == host {
return index
}
}
return -1
}
func getIndexSecret(secretname string, tls []networkingv1.IngressTLS) int {
for index, v := range tls {
if v.SecretName == secretname {
return index
}
}
return -1
}

287
vendor/k8s.io/kubectl/pkg/cmd/create/create_job.go generated vendored Normal file
View File

@ -0,0 +1,287 @@
/*
Copyright 2018 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 create
import (
"context"
"fmt"
"github.com/spf13/cobra"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/cli-runtime/pkg/resource"
batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"k8s.io/utils/ptr"
)
var (
jobLong = templates.LongDesc(i18n.T(`
Create a job with the specified name.`))
jobExample = templates.Examples(i18n.T(`
# Create a job
kubectl create job my-job --image=busybox
# Create a job with a command
kubectl create job my-job --image=busybox -- date
# Create a job from a cron job named "a-cronjob"
kubectl create job test-job --from=cronjob/a-cronjob`))
)
// CreateJobOptions is the command line options for 'create job'
type CreateJobOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
Image string
From string
Command []string
Namespace string
EnforceNamespace bool
Client batchv1client.BatchV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
Builder *resource.Builder
FieldManager string
CreateAnnotation bool
genericiooptions.IOStreams
}
// NewCreateJobOptions initializes and returns new CreateJobOptions instance
func NewCreateJobOptions(ioStreams genericiooptions.IOStreams) *CreateJobOptions {
return &CreateJobOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateJob is a command to ease creating Jobs from CronJobs.
func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewCreateJobOptions(ioStreams)
cmd := &cobra.Command{
Use: "job NAME --image=image [--from=cronjob/name] -- [COMMAND] [args...]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a job with the specified name"),
Long: jobLong,
Example: jobExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
if len(args) > 1 {
o.Command = args[1:]
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = batchv1client.NewForConfig(clientConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.Builder = f.NewBuilder()
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate makes sure provided values and valid Job options
func (o *CreateJobOptions) Validate() error {
if (len(o.Image) == 0 && len(o.From) == 0) || (len(o.Image) != 0 && len(o.From) != 0) {
return fmt.Errorf("either --image or --from must be specified")
}
if o.Command != nil && len(o.Command) != 0 && len(o.From) != 0 {
return fmt.Errorf("cannot specify --from and command")
}
return nil
}
// Run performs the execution of 'create job' sub command
func (o *CreateJobOptions) Run() error {
var job *batchv1.Job
if len(o.Image) > 0 {
job = o.createJob()
} else {
infos, err := o.Builder.
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
NamespaceParam(o.Namespace).DefaultNamespace().
ResourceTypeOrNameArgs(false, o.From).
Flatten().
Latest().
Do().
Infos()
if err != nil {
return err
}
if len(infos) != 1 {
return fmt.Errorf("from must be an existing cronjob")
}
switch obj := infos[0].Object.(type) {
case *batchv1.CronJob:
job = o.createJobFromCronJob(obj)
default:
return fmt.Errorf("unknown object type %T", obj)
}
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, job, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
job, err = o.Client.Jobs(o.Namespace).Create(context.TODO(), job, createOptions)
if err != nil {
return fmt.Errorf("failed to create job: %v", err)
}
}
return o.PrintObj(job)
}
func (o *CreateJobOptions) createJob() *batchv1.Job {
job := &batchv1.Job{
// this is ok because we know exactly how we want to be serialized
TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
},
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: o.Name,
Image: o.Image,
Command: o.Command,
},
},
RestartPolicy: corev1.RestartPolicyNever,
},
},
},
}
if o.EnforceNamespace {
job.Namespace = o.Namespace
}
return job
}
func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1.CronJob) *batchv1.Job {
annotations := make(map[string]string)
annotations["cronjob.kubernetes.io/instantiate"] = "manual"
for k, v := range cronJob.Spec.JobTemplate.Annotations {
annotations[k] = v
}
job := &batchv1.Job{
// this is ok because we know exactly how we want to be serialized
TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Annotations: annotations,
Labels: cronJob.Spec.JobTemplate.Labels,
OwnerReferences: []metav1.OwnerReference{
{
// we are not using metav1.NewControllerRef because it
// sets BlockOwnerDeletion to true which additionally mandates
// cronjobs/finalizer role and not backwards-compatible.
APIVersion: batchv1.SchemeGroupVersion.String(),
Kind: "CronJob",
Name: cronJob.GetName(),
UID: cronJob.GetUID(),
Controller: ptr.To(true),
},
},
},
Spec: cronJob.Spec.JobTemplate.Spec,
}
if o.EnforceNamespace {
job.Namespace = o.Namespace
}
return job
}

View File

@ -0,0 +1,179 @@
/*
Copyright 2015 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 create
import (
"context"
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
namespaceLong = templates.LongDesc(i18n.T(`
Create a namespace with the specified name.`))
namespaceExample = templates.Examples(i18n.T(`
# Create a new namespace named my-namespace
kubectl create namespace my-namespace`))
)
// NamespaceOptions is the options for 'create namespace' sub command
type NamespaceOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
// Name of resource being created
Name string
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
CreateAnnotation bool
FieldManager string
Client *coreclient.CoreV1Client
PrintObj func(obj runtime.Object) error
genericiooptions.IOStreams
}
// NewNamespaceOptions creates a new *NamespaceOptions with sane defaults
func NewNamespaceOptions(ioStreams genericiooptions.IOStreams) *NamespaceOptions {
return &NamespaceOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateNamespace is a macro command to create a new namespace
func NewCmdCreateNamespace(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewNamespaceOptions(ioStreams)
cmd := &cobra.Command{
Use: "namespace NAME [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Aliases: []string{"ns"},
Short: i18n.T("Create a namespace with the specified name"),
Long: namespaceLong,
Example: namespaceExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *NamespaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = coreclient.NewForConfig(restConfig)
if err != nil {
return err
}
o.Name = name
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
return err
}
// Run calls the CreateSubcommandOptions.Run in NamespaceOpts instance
func (o *NamespaceOptions) Run() error {
namespace := o.createNamespace()
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, namespace, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
namespace, err = o.Client.Namespaces().Create(context.TODO(), namespace, createOptions)
if err != nil {
return err
}
}
return o.PrintObj(namespace)
}
// createNamespace outputs a namespace object using the configured fields
func (o *NamespaceOptions) createNamespace() *corev1.Namespace {
namespace := &corev1.Namespace{
TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String(), Kind: "Namespace"},
ObjectMeta: metav1.ObjectMeta{Name: o.Name},
}
return namespace
}
// Validate validates required fields are set to support structured generation
func (o *NamespaceOptions) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
return nil
}

261
vendor/k8s.io/kubectl/pkg/cmd/create/create_pdb.go generated vendored Normal file
View File

@ -0,0 +1,261 @@
/*
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 create
import (
"context"
"fmt"
"regexp"
"github.com/spf13/cobra"
policyv1 "k8s.io/api/policy/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
policyv1client "k8s.io/client-go/kubernetes/typed/policy/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
pdbLong = templates.LongDesc(i18n.T(`
Create a pod disruption budget with the specified name, selector, and desired minimum available pods.`))
pdbExample = templates.Examples(i18n.T(`
# Create a pod disruption budget named my-pdb that will select all pods with the app=rails label
# and require at least one of them being available at any point in time
kubectl create poddisruptionbudget my-pdb --selector=app=rails --min-available=1
# Create a pod disruption budget named my-pdb that will select all pods with the app=nginx label
# and require at least half of the pods selected to be available at any point in time
kubectl create pdb my-pdb --selector=app=nginx --min-available=50%`))
)
// PodDisruptionBudgetOpts holds the command-line options for poddisruptionbudget sub command
type PodDisruptionBudgetOpts struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Name of resource being created
Name string
MinAvailable string
MaxUnavailable string
// A label selector to use for this budget
Selector string
CreateAnnotation bool
FieldManager string
Namespace string
EnforceNamespace bool
Client *policyv1client.PolicyV1Client
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewPodDisruptionBudgetOpts creates a new *PodDisruptionBudgetOpts with sane defaults
func NewPodDisruptionBudgetOpts(ioStreams genericiooptions.IOStreams) *PodDisruptionBudgetOpts {
return &PodDisruptionBudgetOpts{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreatePodDisruptionBudget is a macro command to create a new pod disruption budget.
func NewCmdCreatePodDisruptionBudget(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewPodDisruptionBudgetOpts(ioStreams)
cmd := &cobra.Command{
Use: "poddisruptionbudget NAME --selector=SELECTOR --min-available=N [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Aliases: []string{"pdb"},
Short: i18n.T("Create a pod disruption budget with the specified name"),
Long: pdbLong,
Example: pdbExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.MinAvailable, "min-available", o.MinAvailable, i18n.T("The minimum number or percentage of available pods this budget requires."))
cmd.Flags().StringVar(&o.MaxUnavailable, "max-unavailable", o.MaxUnavailable, i18n.T("The maximum number or percentage of unavailable pods this budget requires."))
cmd.Flags().StringVar(&o.Selector, "selector", o.Selector, i18n.T("A label selector to use for this budget. Only equality-based selector requirements are supported."))
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *PodDisruptionBudgetOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = policyv1client.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks to the PodDisruptionBudgetOpts to see if there is sufficient information run the command
func (o *PodDisruptionBudgetOpts) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(o.Selector) == 0 {
return fmt.Errorf("a selector must be specified")
}
if len(o.MaxUnavailable) == 0 && len(o.MinAvailable) == 0 {
return fmt.Errorf("one of min-available or max-unavailable must be specified")
}
if len(o.MaxUnavailable) > 0 && len(o.MinAvailable) > 0 {
return fmt.Errorf("min-available and max-unavailable cannot be both specified")
}
// The following regex matches the following values:
// 10, 20, 30%, 50% (number and percentage)
// but not 10Gb, 20Mb
re := regexp.MustCompile(`^[0-9]+%?$`)
switch {
case len(o.MinAvailable) > 0 && !re.MatchString(o.MinAvailable):
return fmt.Errorf("invalid format specified for min-available")
case len(o.MaxUnavailable) > 0 && !re.MatchString(o.MaxUnavailable):
return fmt.Errorf("invalid format specified for max-unavailable")
}
return nil
}
// Run calls the CreateSubcommandOptions.Run in PodDisruptionBudgetOpts instance
func (o *PodDisruptionBudgetOpts) Run() error {
podDisruptionBudget, err := o.createPodDisruptionBudgets()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, podDisruptionBudget, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
podDisruptionBudget, err = o.Client.PodDisruptionBudgets(o.Namespace).Create(context.TODO(), podDisruptionBudget, createOptions)
if err != nil {
return fmt.Errorf("failed to create poddisruptionbudgets: %v", err)
}
}
return o.PrintObj(podDisruptionBudget)
}
func (o *PodDisruptionBudgetOpts) createPodDisruptionBudgets() (*policyv1.PodDisruptionBudget, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
podDisruptionBudget := &policyv1.PodDisruptionBudget{
TypeMeta: metav1.TypeMeta{
APIVersion: policyv1.SchemeGroupVersion.String(),
Kind: "PodDisruptionBudget",
},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Namespace: namespace,
},
}
selector, err := metav1.ParseToLabelSelector(o.Selector)
if err != nil {
return nil, err
}
podDisruptionBudget.Spec.Selector = selector
switch {
case len(o.MinAvailable) > 0:
minAvailable := intstr.Parse(o.MinAvailable)
podDisruptionBudget.Spec.MinAvailable = &minAvailable
case len(o.MaxUnavailable) > 0:
maxUnavailable := intstr.Parse(o.MaxUnavailable)
podDisruptionBudget.Spec.MaxUnavailable = &maxUnavailable
}
return podDisruptionBudget, nil
}

View File

@ -0,0 +1,198 @@
/*
Copyright 2017 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 create
import (
"context"
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
schedulingv1 "k8s.io/api/scheduling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
schedulingv1client "k8s.io/client-go/kubernetes/typed/scheduling/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
pcLong = templates.LongDesc(i18n.T(`
Create a priority class with the specified name, value, globalDefault and description.`))
pcExample = templates.Examples(i18n.T(`
# Create a priority class named high-priority
kubectl create priorityclass high-priority --value=1000 --description="high priority"
# Create a priority class named default-priority that is considered as the global default priority
kubectl create priorityclass default-priority --value=1000 --global-default=true --description="default priority"
# Create a priority class named high-priority that cannot preempt pods with lower priority
kubectl create priorityclass high-priority --value=1000 --description="high priority" --preemption-policy="Never"`))
)
// PriorityClassOptions holds the options for 'create priorityclass' sub command
type PriorityClassOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
Value int32
GlobalDefault bool
Description string
PreemptionPolicy string
FieldManager string
CreateAnnotation bool
Client *schedulingv1client.SchedulingV1Client
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewPriorityClassOptions returns an initialized PriorityClassOptions instance
func NewPriorityClassOptions(ioStreams genericiooptions.IOStreams) *PriorityClassOptions {
return &PriorityClassOptions{
Value: 0,
PreemptionPolicy: "PreemptLowerPriority",
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreatePriorityClass is a macro command to create a new priorityClass.
func NewCmdCreatePriorityClass(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewPriorityClassOptions(ioStreams)
cmd := &cobra.Command{
Use: "priorityclass NAME --value=VALUE --global-default=BOOL [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Aliases: []string{"pc"},
Short: i18n.T("Create a priority class with the specified name"),
Long: pcLong,
Example: pcExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().Int32Var(&o.Value, "value", o.Value, i18n.T("the value of this priority class."))
cmd.Flags().BoolVar(&o.GlobalDefault, "global-default", o.GlobalDefault, i18n.T("global-default specifies whether this PriorityClass should be considered as the default priority."))
cmd.Flags().StringVar(&o.Description, "description", o.Description, i18n.T("description is an arbitrary string that usually provides guidelines on when this priority class should be used."))
cmd.Flags().StringVar(&o.PreemptionPolicy, "preemption-policy", o.PreemptionPolicy, i18n.T("preemption-policy is the policy for preempting pods with lower priority."))
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *PriorityClassOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = schedulingv1client.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Run calls the CreateSubcommandOptions.Run in the PriorityClassOptions instance
func (o *PriorityClassOptions) Run() error {
priorityClass, err := o.createPriorityClass()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, priorityClass, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
priorityClass, err = o.Client.PriorityClasses().Create(context.TODO(), priorityClass, createOptions)
if err != nil {
return fmt.Errorf("failed to create priorityclass: %v", err)
}
}
return o.PrintObj(priorityClass)
}
func (o *PriorityClassOptions) createPriorityClass() (*schedulingv1.PriorityClass, error) {
preemptionPolicy := corev1.PreemptionPolicy(o.PreemptionPolicy)
return &schedulingv1.PriorityClass{
// this is ok because we know exactly how we want to be serialized
TypeMeta: metav1.TypeMeta{APIVersion: schedulingv1.SchemeGroupVersion.String(), Kind: "PriorityClass"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
},
Value: o.Value,
GlobalDefault: o.GlobalDefault,
Description: o.Description,
PreemptionPolicy: &preemptionPolicy,
}, nil
}

268
vendor/k8s.io/kubectl/pkg/cmd/create/create_quota.go generated vendored Normal file
View File

@ -0,0 +1,268 @@
/*
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 create
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
resourceapi "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
quotaLong = templates.LongDesc(i18n.T(`
Create a resource quota with the specified name, hard limits, and optional scopes.`))
quotaExample = templates.Examples(i18n.T(`
# Create a new resource quota named my-quota
kubectl create quota my-quota --hard=cpu=1,memory=1G,pods=2,services=3,replicationcontrollers=2,resourcequotas=1,secrets=5,persistentvolumeclaims=10
# Create a new resource quota named best-effort
kubectl create quota best-effort --hard=pods=100 --scopes=BestEffort`))
)
// QuotaOpts holds the command-line options for 'create quota' sub command
type QuotaOpts struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// The name of a quota object.
Name string
// The hard resource limit string before parsing.
Hard string
// The scopes of a quota object before parsing.
Scopes string
CreateAnnotation bool
FieldManager string
Namespace string
EnforceNamespace bool
Client *coreclient.CoreV1Client
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewQuotaOpts creates a new *QuotaOpts with sane defaults
func NewQuotaOpts(ioStreams genericiooptions.IOStreams) *QuotaOpts {
return &QuotaOpts{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateQuota is a macro command to create a new quota
func NewCmdCreateQuota(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewQuotaOpts(ioStreams)
cmd := &cobra.Command{
Use: "quota NAME [--hard=key1=value1,key2=value2] [--scopes=Scope1,Scope2] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Aliases: []string{"resourcequota"},
Short: i18n.T("Create a quota with the specified name"),
Long: quotaLong,
Example: quotaExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.Hard, "hard", o.Hard, i18n.T("A comma-delimited set of resource=quantity pairs that define a hard limit."))
cmd.Flags().StringVar(&o.Scopes, "scopes", o.Scopes, i18n.T("A comma-delimited set of quota scopes that must all match each object tracked by the quota."))
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *QuotaOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = coreclient.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks to the QuotaOpts to see if there is sufficient information run the command.
func (o *QuotaOpts) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
return nil
}
// Run does the work
func (o *QuotaOpts) Run() error {
resourceQuota, err := o.createQuota()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, resourceQuota, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
resourceQuota, err = o.Client.ResourceQuotas(o.Namespace).Create(context.TODO(), resourceQuota, createOptions)
if err != nil {
return fmt.Errorf("failed to create quota: %v", err)
}
}
return o.PrintObj(resourceQuota)
}
func (o *QuotaOpts) createQuota() (*corev1.ResourceQuota, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
resourceQuota := &corev1.ResourceQuota{
TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ResourceQuota"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Namespace: namespace,
},
}
resourceList, err := populateResourceListV1(o.Hard)
if err != nil {
return nil, err
}
scopes, err := parseScopes(o.Scopes)
if err != nil {
return nil, err
}
resourceQuota.Spec.Hard = resourceList
resourceQuota.Spec.Scopes = scopes
return resourceQuota, nil
}
// populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
// and returns ResourceList.
func populateResourceListV1(spec string) (corev1.ResourceList, error) {
// empty input gets a nil response to preserve generator test expected behaviors
if spec == "" {
return nil, nil
}
result := corev1.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 := corev1.ResourceName(parts[0])
resourceQuantity, err := resourceapi.ParseQuantity(parts[1])
if err != nil {
return nil, err
}
result[resourceName] = resourceQuantity
}
return result, nil
}
func parseScopes(spec string) ([]corev1.ResourceQuotaScope, error) {
// empty input gets a nil response to preserve test expected behaviors
if spec == "" {
return nil, nil
}
scopes := strings.Split(spec, ",")
result := make([]corev1.ResourceQuotaScope, 0, len(scopes))
for _, scope := range scopes {
// intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
if scope == "" {
return nil, fmt.Errorf("invalid resource quota scope \"\"")
}
result = append(result, corev1.ResourceQuotaScope(scope))
}
return result, nil
}

445
vendor/k8s.io/kubectl/pkg/cmd/create/create_role.go generated vendored Normal file
View File

@ -0,0 +1,445 @@
/*
Copyright 2017 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 create
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
clientgorbacv1 "k8s.io/client-go/kubernetes/typed/rbac/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
roleLong = templates.LongDesc(i18n.T(`
Create a role with single rule.`))
roleExample = templates.Examples(i18n.T(`
# Create a role named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
# Create a role named "pod-reader" with ResourceName specified
kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
# Create a role named "foo" with API Group specified
kubectl create role foo --verb=get,list,watch --resource=rs.apps
# Create a role named "foo" with SubResource specified
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status`))
// Valid resource verb list for validation.
validResourceVerbs = []string{"*", "get", "delete", "list", "create", "update", "patch", "watch", "proxy", "deletecollection", "use", "bind", "escalate", "impersonate"}
// Specialized verbs and GroupResources
specialVerbs = map[string][]schema.GroupResource{
"use": {
{
Group: "policy",
Resource: "podsecuritypolicies",
},
{
Group: "extensions",
Resource: "podsecuritypolicies",
},
},
"bind": {
{
Group: "rbac.authorization.k8s.io",
Resource: "roles",
},
{
Group: "rbac.authorization.k8s.io",
Resource: "clusterroles",
},
},
"escalate": {
{
Group: "rbac.authorization.k8s.io",
Resource: "roles",
},
{
Group: "rbac.authorization.k8s.io",
Resource: "clusterroles",
},
},
"impersonate": {
{
Group: "",
Resource: "users",
},
{
Group: "",
Resource: "serviceaccounts",
},
{
Group: "",
Resource: "groups",
},
{
Group: "authentication.k8s.io",
Resource: "userextras",
},
},
}
)
// AddSpecialVerb allows the addition of items to the `specialVerbs` map for non-k8s native resources.
func AddSpecialVerb(verb string, gr schema.GroupResource) {
resources, ok := specialVerbs[verb]
if !ok {
resources = make([]schema.GroupResource, 1)
}
resources = append(resources, gr)
specialVerbs[verb] = resources
}
// ResourceOptions holds the related options for '--resource' option
type ResourceOptions struct {
Group string
Resource string
SubResource string
}
// CreateRoleOptions holds the options for 'create role' sub command
type CreateRoleOptions struct {
PrintFlags *genericclioptions.PrintFlags
Name string
Verbs []string
Resources []ResourceOptions
ResourceNames []string
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
OutputFormat string
Namespace string
EnforceNamespace bool
Client clientgorbacv1.RbacV1Interface
Mapper meta.RESTMapper
PrintObj func(obj runtime.Object) error
FieldManager string
CreateAnnotation bool
genericiooptions.IOStreams
}
// NewCreateRoleOptions returns an initialized CreateRoleOptions instance
func NewCreateRoleOptions(ioStreams genericiooptions.IOStreams) *CreateRoleOptions {
return &CreateRoleOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateRole returnns an initialized Command instance for 'create role' sub command
func NewCmdCreateRole(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewCreateRoleOptions(ioStreams)
cmd := &cobra.Command{
Use: "role NAME --verb=verb --resource=resource.group/subresource [--resource-name=resourcename] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a role with single rule"),
Long: roleLong,
Example: roleExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunCreateRole())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringSliceVar(&o.Verbs, "verb", o.Verbs, "Verb that applies to the resources contained in the rule")
cmd.Flags().StringSlice("resource", []string{}, "Resource that the rule applies to")
cmd.Flags().StringArrayVar(&o.ResourceNames, "resource-name", o.ResourceNames, "Resource in the white list that the rule applies to, repeat this flag for multiple items")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
// Remove duplicate verbs.
verbs := []string{}
for _, v := range o.Verbs {
// VerbAll respresents all kinds of verbs.
if v == "*" {
verbs = []string{"*"}
break
}
if !arrayContains(verbs, v) {
verbs = append(verbs, v)
}
}
o.Verbs = verbs
// Support resource.group pattern. If no API Group specified, use "" as core API Group.
// e.g. --resource=pods,deployments.extensions
resources := cmdutil.GetFlagStringSlice(cmd, "resource")
for _, r := range resources {
sections := strings.SplitN(r, "/", 2)
resource := &ResourceOptions{}
if len(sections) == 2 {
resource.SubResource = sections[1]
}
parts := strings.SplitN(sections[0], ".", 2)
if len(parts) == 2 {
resource.Group = parts[1]
}
resource.Resource = parts[0]
if resource.Resource == "*" && len(parts) == 1 && len(sections) == 1 {
o.Resources = []ResourceOptions{*resource}
break
}
o.Resources = append(o.Resources, *resource)
}
// Remove duplicate resource names.
resourceNames := []string{}
for _, n := range o.ResourceNames {
if !arrayContains(resourceNames, n) {
resourceNames = append(resourceNames, n)
}
}
o.ResourceNames = resourceNames
// Complete other options for Run.
o.Mapper, err = f.ToRESTMapper()
if err != nil {
return err
}
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.OutputFormat = cmdutil.GetFlagString(cmd, "output")
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
clientset, err := f.KubernetesClientSet()
if err != nil {
return err
}
o.Client = clientset.RbacV1()
return nil
}
// Validate makes sure there is no discrepency in provided option values
func (o *CreateRoleOptions) Validate() error {
if o.Name == "" {
return fmt.Errorf("name must be specified")
}
// validate verbs.
if len(o.Verbs) == 0 {
return fmt.Errorf("at least one verb must be specified")
}
for _, v := range o.Verbs {
if !arrayContains(validResourceVerbs, v) {
fmt.Fprintf(o.ErrOut, "Warning: '%s' is not a standard resource verb\n", v)
}
}
// validate resources.
if len(o.Resources) == 0 {
return fmt.Errorf("at least one resource must be specified")
}
return o.validateResource()
}
func (o *CreateRoleOptions) validateResource() error {
for _, r := range o.Resources {
if len(r.Resource) == 0 {
return fmt.Errorf("resource must be specified if apiGroup/subresource specified")
}
if r.Resource == "*" {
return nil
}
resource := schema.GroupVersionResource{Resource: r.Resource, Group: r.Group}
groupVersionResource, err := o.Mapper.ResourceFor(schema.GroupVersionResource{Resource: r.Resource, Group: r.Group})
if err == nil {
resource = groupVersionResource
}
for _, v := range o.Verbs {
if groupResources, ok := specialVerbs[v]; ok {
match := false
for _, extra := range groupResources {
if resource.Resource == extra.Resource && resource.Group == extra.Group {
match = true
err = nil
break
}
}
if !match {
return fmt.Errorf("can not perform '%s' on '%s' in group '%s'", v, resource.Resource, resource.Group)
}
}
}
if err != nil {
return err
}
}
return nil
}
// RunCreateRole performs the execution of 'create role' sub command
func (o *CreateRoleOptions) RunCreateRole() error {
role := &rbacv1.Role{
// this is ok because we know exactly how we want to be serialized
TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "Role"},
}
role.Name = o.Name
rules, err := generateResourcePolicyRules(o.Mapper, o.Verbs, o.Resources, o.ResourceNames, []string{})
if err != nil {
return err
}
role.Rules = rules
if o.EnforceNamespace {
role.Namespace = o.Namespace
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, role, scheme.DefaultJSONEncoder()); err != nil {
return err
}
// Create role.
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
role, err = o.Client.Roles(o.Namespace).Create(context.TODO(), role, createOptions)
if err != nil {
return err
}
}
return o.PrintObj(role)
}
func arrayContains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resources []ResourceOptions, resourceNames []string, nonResourceURLs []string) ([]rbacv1.PolicyRule, error) {
// groupResourceMapping is a apigroup-resource map. The key of this map is api group, while the value
// is a string array of resources under this api group.
// E.g. groupResourceMapping = {"extensions": ["replicasets", "deployments"], "batch":["jobs"]}
groupResourceMapping := map[string][]string{}
// This loop does the following work:
// 1. Constructs groupResourceMapping based on input resources.
// 2. Prevents pointing to non-existent resources.
// 3. Transfers resource short name to long name. E.g. rs.extensions is transferred to replicasets.extensions
for _, r := range resources {
resource := schema.GroupVersionResource{Resource: r.Resource, Group: r.Group}
groupVersionResource, err := mapper.ResourceFor(schema.GroupVersionResource{Resource: r.Resource, Group: r.Group})
if err == nil {
resource = groupVersionResource
}
if len(r.SubResource) > 0 {
resource.Resource = resource.Resource + "/" + r.SubResource
}
if !arrayContains(groupResourceMapping[resource.Group], resource.Resource) {
groupResourceMapping[resource.Group] = append(groupResourceMapping[resource.Group], resource.Resource)
}
}
// Create separate rule for each of the api group.
rules := []rbacv1.PolicyRule{}
for _, g := range sets.StringKeySet(groupResourceMapping).List() {
rule := rbacv1.PolicyRule{}
rule.Verbs = verbs
rule.Resources = groupResourceMapping[g]
rule.APIGroups = []string{g}
rule.ResourceNames = resourceNames
rules = append(rules, rule)
}
if len(nonResourceURLs) > 0 {
rule := rbacv1.PolicyRule{}
rule.Verbs = verbs
rule.NonResourceURLs = nonResourceURLs
rules = append(rules, rule)
}
return rules, nil
}

View File

@ -0,0 +1,251 @@
/*
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 create
import (
"context"
"fmt"
"strings"
"github.com/spf13/cobra"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
rbacclientv1 "k8s.io/client-go/kubernetes/typed/rbac/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
roleBindingLong = templates.LongDesc(i18n.T(`
Create a role binding for a particular role or cluster role.`))
roleBindingExample = templates.Examples(i18n.T(`
# Create a role binding for user1, user2, and group1 using the admin cluster role
kubectl create rolebinding admin --clusterrole=admin --user=user1 --user=user2 --group=group1
# Create a role binding for serviceaccount monitoring:sa-dev using the admin role
kubectl create rolebinding admin-binding --role=admin --serviceaccount=monitoring:sa-dev`))
)
// RoleBindingOptions holds the options for 'create rolebinding' sub command
type RoleBindingOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
Namespace string
EnforceNamespace bool
ClusterRole string
Role string
Users []string
Groups []string
ServiceAccounts []string
FieldManager string
CreateAnnotation bool
Client rbacclientv1.RbacV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewRoleBindingOptions creates a new *RoleBindingOptions with sane defaults
func NewRoleBindingOptions(ioStreams genericiooptions.IOStreams) *RoleBindingOptions {
return &RoleBindingOptions{
Users: []string{},
Groups: []string{},
ServiceAccounts: []string{},
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateRoleBinding returns an initialized Command instance for 'create rolebinding' sub command
func NewCmdCreateRoleBinding(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewRoleBindingOptions(ioStreams)
cmd := &cobra.Command{
Use: "rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a role binding for a particular role or cluster role"),
Long: roleBindingLong,
Example: roleBindingExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.ClusterRole, "clusterrole", "", i18n.T("ClusterRole this RoleBinding should reference"))
cmd.Flags().StringVar(&o.Role, "role", "", i18n.T("Role this RoleBinding should reference"))
cmd.Flags().StringArrayVar(&o.Users, "user", o.Users, "Usernames to bind to the role. The flag can be repeated to add multiple users.")
cmd.Flags().StringArrayVar(&o.Groups, "group", o.Groups, "Groups to bind to the role. The flag can be repeated to add multiple groups.")
cmd.Flags().StringArrayVar(&o.ServiceAccounts, "serviceaccount", o.ServiceAccounts, "Service accounts to bind to the role, in the format <namespace>:<name>. The flag can be repeated to add multiple service accounts.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *RoleBindingOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = rbacclientv1.NewForConfig(clientConfig)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
return err
}
// Validate validates required fields are set
func (o *RoleBindingOptions) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if (len(o.ClusterRole) == 0) == (len(o.Role) == 0) {
return fmt.Errorf("exactly one of clusterrole or role must be specified")
}
return nil
}
// Run performs the execution of 'create rolebinding' sub command
func (o *RoleBindingOptions) Run() error {
roleBinding, err := o.createRoleBinding()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, roleBinding, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
roleBinding, err = o.Client.RoleBindings(o.Namespace).Create(context.TODO(), roleBinding, createOptions)
if err != nil {
return fmt.Errorf("failed to create rolebinding: %v", err)
}
}
return o.PrintObj(roleBinding)
}
func (o *RoleBindingOptions) createRoleBinding() (*rbacv1.RoleBinding, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
roleBinding := &rbacv1.RoleBinding{
TypeMeta: metav1.TypeMeta{APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "RoleBinding"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Namespace: namespace,
},
}
switch {
case len(o.Role) > 0:
roleBinding.RoleRef = rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "Role",
Name: o.Role,
}
case len(o.ClusterRole) > 0:
roleBinding.RoleRef = rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "ClusterRole",
Name: o.ClusterRole,
}
}
for _, user := range o.Users {
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
Kind: rbacv1.UserKind,
APIGroup: rbacv1.GroupName,
Name: user,
})
}
for _, group := range o.Groups {
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
Kind: rbacv1.GroupKind,
APIGroup: rbacv1.GroupName,
Name: group,
})
}
for _, sa := range o.ServiceAccounts {
tokens := strings.Split(sa, ":")
if len(tokens) != 2 || tokens[0] == "" || tokens[1] == "" {
return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
}
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
Kind: rbacv1.ServiceAccountKind,
APIGroup: "",
Namespace: tokens[0],
Name: tokens[1],
})
}
return roleBinding, nil
}

421
vendor/k8s.io/kubectl/pkg/cmd/create/create_secret.go generated vendored Normal file
View File

@ -0,0 +1,421 @@
/*
Copyright 2015 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 create
import (
"context"
"fmt"
"os"
"path"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/hash"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
// NewCmdCreateSecret groups subcommands to create various types of secrets.
// This is the entry point of create_secret.go which will be called by create.go
func NewCmdCreateSecret(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "secret (docker-registry | generic | tls)",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a secret using a specified subcommand"),
Long: secretLong,
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
cmd.AddCommand(NewCmdCreateSecretDockerRegistry(f, ioStreams))
cmd.AddCommand(NewCmdCreateSecretTLS(f, ioStreams))
cmd.AddCommand(NewCmdCreateSecretGeneric(f, ioStreams))
return cmd
}
var (
secretLong = templates.LongDesc(i18n.T(`
Create a secret with specified type.
A docker-registry type secret is for accessing a container registry.
A generic type secret indicate an Opaque secret type.
A tls type secret holds TLS certificate and its associated key.`))
secretForGenericLong = templates.LongDesc(i18n.T(`
Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs.
When creating a secret based on a file, the key will default to the basename of the file, and the value will
default to the file content. If the basename is an invalid key or you wish to chose your own, you may specify
an alternate key.
When creating a secret based on a directory, each file whose basename is a valid key in the directory will be
packaged into the secret. Any directory entries except regular files are ignored (e.g. subdirectories,
symlinks, devices, pipes, etc).`))
secretForGenericExample = templates.Examples(i18n.T(`
# Create a new secret named my-secret with keys for each file in folder bar
kubectl create secret generic my-secret --from-file=path/to/bar
# Create a new secret named my-secret with specified keys instead of names on disk
kubectl create secret generic my-secret --from-file=ssh-privatekey=path/to/id_rsa --from-file=ssh-publickey=path/to/id_rsa.pub
# Create a new secret named my-secret with key1=supersecret and key2=topsecret
kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret
# Create a new secret named my-secret using a combination of a file and a literal
kubectl create secret generic my-secret --from-file=ssh-privatekey=path/to/id_rsa --from-literal=passphrase=topsecret
# Create a new secret named my-secret from env files
kubectl create secret generic my-secret --from-env-file=path/to/foo.env --from-env-file=path/to/bar.env`))
)
// CreateSecretOptions holds the options for 'create secret' sub command
type CreateSecretOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Name of secret (required)
Name string
// Type of secret (optional)
Type string
// FileSources to derive the secret from (optional)
FileSources []string
// LiteralSources to derive the secret from (optional)
LiteralSources []string
// EnvFileSources to derive the secret from (optional)
EnvFileSources []string
// AppendHash; if true, derive a hash from the Secret data and type and append it to the name
AppendHash bool
FieldManager string
CreateAnnotation bool
Namespace string
EnforceNamespace bool
Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewSecretOptions creates a new *CreateSecretOptions with default value
func NewSecretOptions(ioStreams genericiooptions.IOStreams) *CreateSecretOptions {
return &CreateSecretOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateSecretGeneric is a command to create generic secrets from files, directories, or literal values
func NewCmdCreateSecretGeneric(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewSecretOptions(ioStreams)
cmd := &cobra.Command{
Use: "generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a secret from a local file, directory, or literal value"),
Long: secretForGenericLong,
Example: secretForGenericExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringSliceVar(&o.FileSources, "from-file", o.FileSources, "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid secret key.")
cmd.Flags().StringArrayVar(&o.LiteralSources, "from-literal", o.LiteralSources, "Specify a key and literal value to insert in secret (i.e. mykey=somevalue)")
cmd.Flags().StringSliceVar(&o.EnvFileSources, "from-env-file", o.EnvFileSources, "Specify the path to a file to read lines of key=val pairs to create a secret.")
cmd.Flags().StringVar(&o.Type, "type", o.Type, i18n.T("The type of secret to create"))
cmd.Flags().BoolVar(&o.AppendHash, "append-hash", o.AppendHash, "Append a hash of the secret to its name.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete loads data from the command line environment
func (o *CreateSecretOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = corev1client.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks if CreateSecretOptions has sufficient value to run
func (o *CreateSecretOptions) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(o.EnvFileSources) > 0 && (len(o.FileSources) > 0 || len(o.LiteralSources) > 0) {
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
}
return nil
}
// Run calls createSecret which will create secret based on CreateSecretOptions
// and makes an API call to the server
func (o *CreateSecretOptions) Run() error {
secret, err := o.createSecret()
if err != nil {
return err
}
err = util.CreateOrUpdateAnnotation(o.CreateAnnotation, secret, scheme.DefaultJSONEncoder())
if err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
secret, err = o.Client.Secrets(o.Namespace).Create(context.TODO(), secret, createOptions)
if err != nil {
return fmt.Errorf("failed to create secret %v", err)
}
}
return o.PrintObj(secret)
}
// createSecret fills in key value pair from the information given in
// CreateSecretOptions into *corev1.Secret
func (o *CreateSecretOptions) createSecret() (*corev1.Secret, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
secret := newSecretObj(o.Name, namespace, corev1.SecretType(o.Type))
if len(o.LiteralSources) > 0 {
if err := handleSecretFromLiteralSources(secret, o.LiteralSources); err != nil {
return nil, err
}
}
if len(o.FileSources) > 0 {
if err := handleSecretFromFileSources(secret, o.FileSources); err != nil {
return nil, err
}
}
if len(o.EnvFileSources) > 0 {
if err := handleSecretFromEnvFileSources(secret, o.EnvFileSources); err != nil {
return nil, err
}
}
if o.AppendHash {
hash, err := hash.SecretHash(secret)
if err != nil {
return nil, err
}
secret.Name = fmt.Sprintf("%s-%s", secret.Name, hash)
}
return secret, nil
}
// newSecretObj will create a new Secret Object given name, namespace and secretType
func newSecretObj(name, namespace string, secretType corev1.SecretType) *corev1.Secret {
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Type: secretType,
Data: map[string][]byte{},
}
}
// handleSecretFromLiteralSources adds the specified literal source
// information into the provided secret
func handleSecretFromLiteralSources(secret *corev1.Secret, literalSources []string) error {
for _, literalSource := range literalSources {
keyName, value, err := util.ParseLiteralSource(literalSource)
if err != nil {
return err
}
if err = addKeyFromLiteralToSecret(secret, keyName, []byte(value)); err != nil {
return err
}
}
return nil
}
// handleSecretFromFileSources adds the specified file source information into the provided secret
func handleSecretFromFileSources(secret *corev1.Secret, fileSources []string) error {
for _, fileSource := range fileSources {
keyName, filePath, err := util.ParseFileSource(fileSource)
if err != nil {
return err
}
fileInfo, err := os.Stat(filePath)
if err != nil {
switch err := err.(type) {
case *os.PathError:
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
default:
return fmt.Errorf("error reading %s: %v", filePath, err)
}
}
// if the filePath is a directory
if fileInfo.IsDir() {
if strings.Contains(fileSource, "=") {
return fmt.Errorf("cannot give a key name for a directory path")
}
fileList, err := os.ReadDir(filePath)
if err != nil {
return fmt.Errorf("error listing files in %s: %v", filePath, err)
}
for _, item := range fileList {
itemPath := path.Join(filePath, item.Name())
if item.Type().IsRegular() {
keyName = item.Name()
if err := addKeyFromFileToSecret(secret, keyName, itemPath); err != nil {
return err
}
}
}
// if the filepath is a file
} else {
if err := addKeyFromFileToSecret(secret, keyName, filePath); err != nil {
return err
}
}
}
return nil
}
// handleSecretFromEnvFileSources adds the specified env files source information
// into the provided secret
func handleSecretFromEnvFileSources(secret *corev1.Secret, envFileSources []string) error {
for _, envFileSource := range envFileSources {
info, err := os.Stat(envFileSource)
if err != nil {
switch err := err.(type) {
case *os.PathError:
return fmt.Errorf("error reading %s: %v", envFileSource, err.Err)
default:
return fmt.Errorf("error reading %s: %v", envFileSource, err)
}
}
if info.IsDir() {
return fmt.Errorf("env secret file cannot be a directory")
}
err = cmdutil.AddFromEnvFile(envFileSource, func(key, value string) error {
return addKeyFromLiteralToSecret(secret, key, []byte(value))
})
if err != nil {
return err
}
}
return nil
}
// addKeyFromFileToSecret adds a key with the given name to a Secret, populating
// the value with the content of the given file path, or returns an error.
func addKeyFromFileToSecret(secret *corev1.Secret, keyName, filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return err
}
return addKeyFromLiteralToSecret(secret, keyName, data)
}
// addKeyFromLiteralToSecret adds the given key and data to the given secret,
// returning an error if the key is not valid or if the key already exists.
func addKeyFromLiteralToSecret(secret *corev1.Secret, keyName string, data []byte) error {
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
return fmt.Errorf("%q is not valid key name for a Secret %s", keyName, strings.Join(errs, ";"))
}
if _, entryExists := secret.Data[keyName]; entryExists {
return fmt.Errorf("cannot add key %s, another key by that name already exists", keyName)
}
secret.Data[keyName] = data
return nil
}

View File

@ -0,0 +1,298 @@
/*
Copyright 2021 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 create
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/hash"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
secretForDockerRegistryLong = templates.LongDesc(i18n.T(`
Create a new secret for use with Docker registries.
Dockercfg secrets are used to authenticate against Docker registries.
When using the Docker command line to push images, you can authenticate to a given registry by running:
'$ docker login DOCKER_REGISTRY_SERVER --username=DOCKER_USER --password=DOCKER_PASSWORD --email=DOCKER_EMAIL'.
That produces a ~/.dockercfg file that is used by subsequent 'docker push' and 'docker pull' commands to
authenticate to the registry. The email address is optional.
When creating applications, you may have a Docker registry that requires authentication. In order for the
nodes to pull images on your behalf, they must have the credentials. You can provide this information
by creating a dockercfg secret and attaching it to your service account.`))
secretForDockerRegistryExample = templates.Examples(i18n.T(`
# If you do not already have a .dockercfg file, create a dockercfg secret directly
kubectl create secret docker-registry my-secret --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
# Create a new secret named my-secret from ~/.docker/config.json
kubectl create secret docker-registry my-secret --from-file=.dockerconfigjson=path/to/.docker/config.json`))
)
// DockerConfigJSON represents a local docker auth config file
// for pulling images.
type DockerConfigJSON struct {
Auths DockerConfig `json:"auths" datapolicy:"token"`
// +optional
HttpHeaders map[string]string `json:"HttpHeaders,omitempty" datapolicy:"token"`
}
// DockerConfig represents the config file used by the docker CLI.
// This config that represents the credentials that should be used
// when pulling images from specific image repositories.
type DockerConfig map[string]DockerConfigEntry
// DockerConfigEntry holds the user information that grant the access to docker registry
type DockerConfigEntry struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty" datapolicy:"password"`
Email string `json:"email,omitempty"`
Auth string `json:"auth,omitempty" datapolicy:"token"`
}
// CreateSecretDockerRegistryOptions holds the options for 'create secret docker-registry' sub command
type CreateSecretDockerRegistryOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Name of secret (required)
Name string
// FileSources to derive the secret from (optional)
FileSources []string
// Username for registry (required)
Username string
// Email for registry (optional)
Email string
// Password for registry (required)
Password string `datapolicy:"password"`
// Server for registry (required)
Server string
// AppendHash; if true, derive a hash from the Secret and append it to the name
AppendHash bool
FieldManager string
CreateAnnotation bool
Namespace string
EnforceNamespace bool
Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewSecretDockerRegistryOptions creates a new *CreateSecretDockerRegistryOptions with default value
func NewSecretDockerRegistryOptions(ioStreams genericiooptions.IOStreams) *CreateSecretDockerRegistryOptions {
return &CreateSecretDockerRegistryOptions{
Server: "https://index.docker.io/v1/",
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateSecretDockerRegistry is a macro command for creating secrets to work with Docker registries
func NewCmdCreateSecretDockerRegistry(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewSecretDockerRegistryOptions(ioStreams)
cmd := &cobra.Command{
Use: "docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-file=[key=]source] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a secret for use with a Docker registry"),
Long: secretForDockerRegistryLong,
Example: secretForDockerRegistryExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.Username, "docker-username", o.Username, i18n.T("Username for Docker registry authentication"))
cmd.Flags().StringVar(&o.Password, "docker-password", o.Password, i18n.T("Password for Docker registry authentication"))
cmd.Flags().StringVar(&o.Email, "docker-email", o.Email, i18n.T("Email for Docker registry"))
cmd.Flags().StringVar(&o.Server, "docker-server", o.Server, i18n.T("Server location for Docker registry"))
cmd.Flags().BoolVar(&o.AppendHash, "append-hash", o.AppendHash, "Append a hash of the secret to its name.")
cmd.Flags().StringSliceVar(&o.FileSources, "from-file", o.FileSources, "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid secret key.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete loads data from the command line environment
func (o *CreateSecretDockerRegistryOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = corev1client.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks if CreateSecretDockerRegistryOptions has sufficient value to run
func (o *CreateSecretDockerRegistryOptions) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(o.FileSources) == 0 && (len(o.Username) == 0 || len(o.Password) == 0 || len(o.Server) == 0) {
return fmt.Errorf("either --from-file or the combination of --docker-username, --docker-password and --docker-server is required")
}
return nil
}
// Run calls createSecretDockerRegistry which will create secretDockerRegistry based on CreateSecretDockerRegistryOptions
// and makes an API call to the server
func (o *CreateSecretDockerRegistryOptions) Run() error {
secretDockerRegistry, err := o.createSecretDockerRegistry()
if err != nil {
return err
}
err = util.CreateOrUpdateAnnotation(o.CreateAnnotation, secretDockerRegistry, scheme.DefaultJSONEncoder())
if err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
secretDockerRegistry, err = o.Client.Secrets(o.Namespace).Create(context.TODO(), secretDockerRegistry, createOptions)
if err != nil {
return fmt.Errorf("failed to create secret %v", err)
}
}
return o.PrintObj(secretDockerRegistry)
}
// createSecretDockerRegistry fills in key value pair from the information given in
// CreateSecretDockerRegistryOptions into *corev1.Secret
func (o *CreateSecretDockerRegistryOptions) createSecretDockerRegistry() (*corev1.Secret, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
secretDockerRegistry := newSecretObj(o.Name, namespace, corev1.SecretTypeDockerConfigJson)
if len(o.FileSources) > 0 {
if err := handleSecretFromFileSources(secretDockerRegistry, o.FileSources); err != nil {
return nil, err
}
} else {
dockerConfigJSONContent, err := handleDockerCfgJSONContent(o.Username, o.Password, o.Email, o.Server)
if err != nil {
return nil, err
}
secretDockerRegistry.Data[corev1.DockerConfigJsonKey] = dockerConfigJSONContent
}
if o.AppendHash {
hash, err := hash.SecretHash(secretDockerRegistry)
if err != nil {
return nil, err
}
secretDockerRegistry.Name = fmt.Sprintf("%s-%s", secretDockerRegistry.Name, hash)
}
return secretDockerRegistry, nil
}
// handleDockerCfgJSONContent serializes a ~/.docker/config.json file
func handleDockerCfgJSONContent(username, password, email, server string) ([]byte, error) {
dockerConfigAuth := DockerConfigEntry{
Username: username,
Password: password,
Email: email,
Auth: encodeDockerConfigFieldAuth(username, password),
}
dockerConfigJSON := DockerConfigJSON{
Auths: map[string]DockerConfigEntry{server: dockerConfigAuth},
}
return json.Marshal(dockerConfigJSON)
}
// encodeDockerConfigFieldAuth returns base64 encoding of the username and password string
func encodeDockerConfigFieldAuth(username, password string) string {
fieldValue := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(fieldValue))
}

View File

@ -0,0 +1,249 @@
/*
Copyright 2021 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 create
import (
"context"
"crypto/tls"
"fmt"
"os"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/hash"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
secretForTLSLong = templates.LongDesc(i18n.T(`
Create a TLS secret from the given public/private key pair.
The public/private key pair must exist beforehand. The public key certificate must be .PEM encoded and match
the given private key.`))
secretForTLSExample = templates.Examples(i18n.T(`
# Create a new TLS secret named tls-secret with the given key pair
kubectl create secret tls tls-secret --cert=path/to/tls.crt --key=path/to/tls.key`))
)
// CreateSecretTLSOptions holds the options for 'create secret tls' sub command
type CreateSecretTLSOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Name is the name of this TLS secret.
Name string
// Key is the path to the user's private key.
Key string
// Cert is the path to the user's public key certificate.
Cert string
// AppendHash; if true, derive a hash from the Secret and append it to the name
AppendHash bool
FieldManager string
CreateAnnotation bool
Namespace string
EnforceNamespace bool
Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewSecretTLSOptions creates a new *CreateSecretTLSOptions with default value
func NewSecretTLSOptions(ioStrems genericiooptions.IOStreams) *CreateSecretTLSOptions {
return &CreateSecretTLSOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStrems,
}
}
// NewCmdCreateSecretTLS is a macro command for creating secrets to work with TLS client or server
func NewCmdCreateSecretTLS(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewSecretTLSOptions(ioStreams)
cmd := &cobra.Command{
Use: "tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a TLS secret"),
Long: secretForTLSLong,
Example: secretForTLSExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.Cert, "cert", o.Cert, i18n.T("Path to PEM encoded public key certificate."))
cmd.Flags().StringVar(&o.Key, "key", o.Key, i18n.T("Path to private key associated with given certificate."))
cmd.Flags().BoolVar(&o.AppendHash, "append-hash", o.AppendHash, "Append a hash of the secret to its name.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete loads data from the command line environment
func (o *CreateSecretTLSOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = corev1client.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks if CreateSecretTLSOptions hass sufficient value to run
func (o *CreateSecretTLSOptions) Validate() error {
// TODO: This is not strictly necessary. We can generate a self signed cert
// if no key/cert is given. The only requirement is that we either get both
// or none. See test/e2e/ingress_utils for self signed cert generation.
if len(o.Key) == 0 || len(o.Cert) == 0 {
return fmt.Errorf("key and cert must be specified")
}
return nil
}
// Run calls createSecretTLS which will create secretTLS based on CreateSecretTLSOptions
// and makes an API call to the server
func (o *CreateSecretTLSOptions) Run() error {
secretTLS, err := o.createSecretTLS()
if err != nil {
return err
}
err = util.CreateOrUpdateAnnotation(o.CreateAnnotation, secretTLS, scheme.DefaultJSONEncoder())
if err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
secretTLS, err = o.Client.Secrets(o.Namespace).Create(context.TODO(), secretTLS, createOptions)
if err != nil {
return fmt.Errorf("failed to create secret %v", err)
}
}
return o.PrintObj(secretTLS)
}
// createSecretTLS fills in key value pair from the information given in
// CreateSecretTLSOptions into *corev1.Secret
func (o *CreateSecretTLSOptions) createSecretTLS() (*corev1.Secret, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
tlsCert, err := readFile(o.Cert)
if err != nil {
return nil, err
}
tlsKey, err := readFile(o.Key)
if err != nil {
return nil, err
}
if _, err := tls.X509KeyPair(tlsCert, tlsKey); err != nil {
return nil, err
}
// TODO: Add more validation.
// 1. If the certificate contains intermediates, it is a valid chain.
// 2. Format etc.
secretTLS := newSecretObj(o.Name, namespace, corev1.SecretTypeTLS)
secretTLS.Data[corev1.TLSCertKey] = []byte(tlsCert)
secretTLS.Data[corev1.TLSPrivateKeyKey] = []byte(tlsKey)
if o.AppendHash {
hash, err := hash.SecretHash(secretTLS)
if err != nil {
return nil, err
}
secretTLS.Name = fmt.Sprintf("%s-%s", secretTLS.Name, hash)
}
return secretTLS, nil
}
// readFile just reads a file into a byte array.
func readFile(file string) ([]byte, error) {
b, err := os.ReadFile(file)
if err != nil {
return []byte{}, fmt.Errorf("Cannot read file %v, %v", file, err)
}
return b, nil
}

412
vendor/k8s.io/kubectl/pkg/cmd/create/create_service.go generated vendored Normal file
View File

@ -0,0 +1,412 @@
/*
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 create
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
utilsnet "k8s.io/utils/net"
)
// NewCmdCreateService is a macro command to create a new service
func NewCmdCreateService(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "service",
Aliases: []string{"svc"},
Short: i18n.T("Create a service using a specified subcommand"),
Long: i18n.T("Create a service using a specified subcommand."),
Run: cmdutil.DefaultSubCommandRun(ioStreams.ErrOut),
}
cmd.AddCommand(NewCmdCreateServiceClusterIP(f, ioStreams))
cmd.AddCommand(NewCmdCreateServiceNodePort(f, ioStreams))
cmd.AddCommand(NewCmdCreateServiceLoadBalancer(f, ioStreams))
cmd.AddCommand(NewCmdCreateServiceExternalName(f, ioStreams))
return cmd
}
// ServiceOptions holds the options for 'create service' sub command
type ServiceOptions struct {
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
Name string
TCP []string
Type corev1.ServiceType
ClusterIP string
NodePort int
ExternalName string
FieldManager string
CreateAnnotation bool
Namespace string
EnforceNamespace bool
Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
genericiooptions.IOStreams
}
// NewServiceOptions creates a ServiceOptions struct
func NewServiceOptions(ioStreams genericiooptions.IOStreams, serviceType corev1.ServiceType) *ServiceOptions {
return &ServiceOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
Type: serviceType,
}
}
// Complete completes all the required options
func (o *ServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Name = name
clientConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = corev1client.NewForConfig(clientConfig)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate if the options are valid
func (o *ServiceOptions) Validate() error {
if o.ClusterIP == corev1.ClusterIPNone && o.Type != corev1.ServiceTypeClusterIP {
return fmt.Errorf("ClusterIP=None can only be used with ClusterIP service type")
}
if o.ClusterIP != corev1.ClusterIPNone && len(o.TCP) == 0 && o.Type != corev1.ServiceTypeExternalName {
return fmt.Errorf("at least one tcp port specifier must be provided")
}
if o.Type == corev1.ServiceTypeExternalName {
if errs := validation.IsDNS1123Subdomain(o.ExternalName); len(errs) != 0 {
return fmt.Errorf("invalid service external name %s", o.ExternalName)
}
}
return nil
}
func (o *ServiceOptions) createService() (*corev1.Service, error) {
ports := []corev1.ServicePort{}
for _, tcpString := range o.TCP {
port, targetPort, err := parsePorts(tcpString)
if err != nil {
return nil, err
}
portName := strings.Replace(tcpString, ":", "-", -1)
ports = append(ports, corev1.ServicePort{
Name: portName,
Port: port,
TargetPort: targetPort,
Protocol: corev1.Protocol("TCP"),
NodePort: int32(o.NodePort),
})
}
// setup default label and selector
labels := map[string]string{}
labels["app"] = o.Name
selector := map[string]string{}
selector["app"] = o.Name
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
service := corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Labels: labels,
Namespace: namespace,
},
Spec: corev1.ServiceSpec{
Type: o.Type,
Selector: selector,
Ports: ports,
ExternalName: o.ExternalName,
},
}
if len(o.ClusterIP) > 0 {
service.Spec.ClusterIP = o.ClusterIP
}
return &service, nil
}
// Run the service command
func (o *ServiceOptions) Run() error {
service, err := o.createService()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, service, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
var err error
service, err = o.Client.Services(o.Namespace).Create(context.TODO(), service, createOptions)
if err != nil {
return fmt.Errorf("failed to create %s service: %v", o.Type, err)
}
}
return o.PrintObj(service)
}
var (
serviceClusterIPLong = templates.LongDesc(i18n.T(`
Create a ClusterIP service with the specified name.`))
serviceClusterIPExample = templates.Examples(i18n.T(`
# Create a new ClusterIP service named my-cs
kubectl create service clusterip my-cs --tcp=5678:8080
# Create a new ClusterIP service named my-cs (in headless mode)
kubectl create service clusterip my-cs --clusterip="None"`))
)
// NewCmdCreateServiceClusterIP is a command to create a ClusterIP service
func NewCmdCreateServiceClusterIP(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewServiceOptions(ioStreams, corev1.ServiceTypeClusterIP)
cmd := &cobra.Command{
Use: "clusterip NAME [--tcp=<port>:<targetPort>] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a ClusterIP service"),
Long: serviceClusterIPLong,
Example: serviceClusterIPExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmd.Flags().StringSliceVar(&o.TCP, "tcp", o.TCP, "Port pairs can be specified as '<port>:<targetPort>'.")
cmd.Flags().StringVar(&o.ClusterIP, "clusterip", o.ClusterIP, i18n.T("Assign your own ClusterIP or set to 'None' for a 'headless' service (no loadbalancing)."))
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
cmdutil.AddDryRunFlag(cmd)
return cmd
}
var (
serviceNodePortLong = templates.LongDesc(i18n.T(`
Create a NodePort service with the specified name.`))
serviceNodePortExample = templates.Examples(i18n.T(`
# Create a new NodePort service named my-ns
kubectl create service nodeport my-ns --tcp=5678:8080`))
)
// NewCmdCreateServiceNodePort is a macro command for creating a NodePort service
func NewCmdCreateServiceNodePort(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewServiceOptions(ioStreams, corev1.ServiceTypeNodePort)
cmd := &cobra.Command{
Use: "nodeport NAME [--tcp=port:targetPort] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a NodePort service"),
Long: serviceNodePortLong,
Example: serviceNodePortExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmd.Flags().IntVar(&o.NodePort, "node-port", o.NodePort, "Port used to expose the service on each node in a cluster.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
cmd.Flags().StringSliceVar(&o.TCP, "tcp", o.TCP, "Port pairs can be specified as '<port>:<targetPort>'.")
cmdutil.AddDryRunFlag(cmd)
return cmd
}
var (
serviceLoadBalancerLong = templates.LongDesc(i18n.T(`
Create a LoadBalancer service with the specified name.`))
serviceLoadBalancerExample = templates.Examples(i18n.T(`
# Create a new LoadBalancer service named my-lbs
kubectl create service loadbalancer my-lbs --tcp=5678:8080`))
)
// NewCmdCreateServiceLoadBalancer is a macro command for creating a LoadBalancer service
func NewCmdCreateServiceLoadBalancer(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewServiceOptions(ioStreams, corev1.ServiceTypeLoadBalancer)
cmd := &cobra.Command{
Use: "loadbalancer NAME [--tcp=port:targetPort] [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create a LoadBalancer service"),
Long: serviceLoadBalancerLong,
Example: serviceLoadBalancerExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmd.Flags().StringSliceVar(&o.TCP, "tcp", o.TCP, "Port pairs can be specified as '<port>:<targetPort>'.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
cmdutil.AddDryRunFlag(cmd)
return cmd
}
var (
serviceExternalNameLong = templates.LongDesc(i18n.T(`
Create an ExternalName service with the specified name.
ExternalName service references to an external DNS address instead of
only pods, which will allow application authors to reference services
that exist off platform, on other clusters, or locally.`))
serviceExternalNameExample = templates.Examples(i18n.T(`
# Create a new ExternalName service named my-ns
kubectl create service externalname my-ns --external-name bar.com`))
)
// NewCmdCreateServiceExternalName is a macro command for creating an ExternalName service
func NewCmdCreateServiceExternalName(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewServiceOptions(ioStreams, corev1.ServiceTypeExternalName)
cmd := &cobra.Command{
Use: "externalname NAME --external-name external.name [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Short: i18n.T("Create an ExternalName service"),
Long: serviceExternalNameLong,
Example: serviceExternalNameExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmd.Flags().StringSliceVar(&o.TCP, "tcp", o.TCP, "Port pairs can be specified as '<port>:<targetPort>'.")
cmd.Flags().StringVar(&o.ExternalName, "external-name", o.ExternalName, i18n.T("External name of service"))
cmd.MarkFlagRequired("external-name")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
cmdutil.AddDryRunFlag(cmd)
return cmd
}
func parsePorts(portString string) (int32, intstr.IntOrString, error) {
portStringSlice := strings.Split(portString, ":")
port, err := utilsnet.ParsePort(portStringSlice[0], true)
if err != nil {
return 0, intstr.FromInt32(0), err
}
if len(portStringSlice) == 1 {
port32 := int32(port)
return port32, intstr.FromInt32(port32), nil
}
var targetPort intstr.IntOrString
if portNum, err := strconv.Atoi(portStringSlice[1]); err != nil {
if errs := validation.IsValidPortName(portStringSlice[1]); len(errs) != 0 {
return 0, intstr.FromInt32(0), fmt.Errorf(strings.Join(errs, ","))
}
targetPort = intstr.FromString(portStringSlice[1])
} else {
if errs := validation.IsValidPortNum(portNum); len(errs) != 0 {
return 0, intstr.FromInt32(0), fmt.Errorf(strings.Join(errs, ","))
}
targetPort = intstr.FromInt32(int32(portNum))
}
return int32(port), targetPort, nil
}

View File

@ -0,0 +1,202 @@
/*
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 create
import (
"context"
"fmt"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
serviceAccountLong = templates.LongDesc(i18n.T(`
Create a service account with the specified name.`))
serviceAccountExample = templates.Examples(i18n.T(`
# Create a new service account named my-service-account
kubectl create serviceaccount my-service-account`))
)
// ServiceAccountOpts holds the options for 'create serviceaccount' sub command
type ServiceAccountOpts struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Name of resource being created
Name string
DryRunStrategy cmdutil.DryRunStrategy
ValidationDirective string
CreateAnnotation bool
FieldManager string
Namespace string
EnforceNamespace bool
Mapper meta.RESTMapper
Client *coreclient.CoreV1Client
genericiooptions.IOStreams
}
// NewServiceAccountOpts creates a new *ServiceAccountOpts with sane defaults
func NewServiceAccountOpts(ioStreams genericiooptions.IOStreams) *ServiceAccountOpts {
return &ServiceAccountOpts{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateServiceAccount is a macro command to create a new service account
func NewCmdCreateServiceAccount(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewServiceAccountOpts(ioStreams)
cmd := &cobra.Command{
Use: "serviceaccount NAME [--dry-run=server|client|none]",
DisableFlagsInUseLine: true,
Aliases: []string{"sa"},
Short: i18n.T("Create a service account with the specified name"),
Long: serviceAccountLong,
Example: serviceAccountExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}
// Complete completes all the required options
func (o *ServiceAccountOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
o.Client, err = coreclient.NewForConfig(restConfig)
if err != nil {
return err
}
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil
}
// Validate checks ServiceAccountOpts to see if there is sufficient information run the command.
func (o *ServiceAccountOpts) Validate() error {
if len(o.Name) == 0 {
return fmt.Errorf("name must be specified")
}
return nil
}
// Run makes the api call to the server
func (o *ServiceAccountOpts) Run() error {
serviceAccount, err := o.createServiceAccount()
if err != nil {
return err
}
if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, serviceAccount, scheme.DefaultJSONEncoder()); err != nil {
return err
}
if o.DryRunStrategy != cmdutil.DryRunClient {
createOptions := metav1.CreateOptions{}
if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager
}
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll}
}
serviceAccount, err = o.Client.ServiceAccounts(o.Namespace).Create(context.TODO(), serviceAccount, createOptions)
if err != nil {
return fmt.Errorf("failed to create serviceaccount: %v", err)
}
}
return o.PrintObj(serviceAccount)
}
func (o *ServiceAccountOpts) createServiceAccount() (*corev1.ServiceAccount, error) {
namespace := ""
if o.EnforceNamespace {
namespace = o.Namespace
}
serviceAccount := &corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ServiceAccount"},
ObjectMeta: metav1.ObjectMeta{
Name: o.Name,
Namespace: namespace,
},
}
serviceAccount.Name = o.Name
return serviceAccount, nil
}

282
vendor/k8s.io/kubectl/pkg/cmd/create/create_token.go generated vendored Normal file
View File

@ -0,0 +1,282 @@
/*
Copyright 2022 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 create
import (
"context"
"fmt"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
authenticationv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/templates"
"k8s.io/kubectl/pkg/util/term"
"k8s.io/utils/pointer"
)
// TokenOptions is the data required to perform a token request operation.
type TokenOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error
// Flags hold the parsed CLI flags.
Flags *pflag.FlagSet
// Name and namespace of service account to create a token for
Name string
Namespace string
// BoundObjectKind is the kind of object to bind the token to. Optional. Can be Pod or Secret.
BoundObjectKind string
// BoundObjectName is the name of the object to bind the token to. Required if BoundObjectKind is set.
BoundObjectName string
// BoundObjectUID is the uid of the object to bind the token to. If unset, defaults to the current uid of the bound object.
BoundObjectUID string
// Audiences indicate the valid audiences for the requested token. If unset, defaults to the Kubernetes API server audiences.
Audiences []string
// Duration is the requested token lifetime. Optional.
Duration time.Duration
// CoreClient is the API client used to request the token. Required.
CoreClient corev1client.CoreV1Interface
// IOStreams are the output streams for the operation. Required.
genericiooptions.IOStreams
}
var (
tokenLong = templates.LongDesc(`Request a service account token.`)
tokenExample = templates.Examples(`
# Request a token to authenticate to the kube-apiserver as the service account "myapp" in the current namespace
kubectl create token myapp
# Request a token for a service account in a custom namespace
kubectl create token myapp --namespace myns
# Request a token with a custom expiration
kubectl create token myapp --duration 10m
# Request a token with a custom audience
kubectl create token myapp --audience https://example.com
# Request a token bound to an instance of a Secret object
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret
# Request a token bound to an instance of a Secret object with a specific UID
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret --bound-object-uid 0d4691ed-659b-4935-a832-355f77ee47cc
`)
)
func boundObjectKindToAPIVersions() map[string]string {
kinds := map[string]string{
"Pod": "v1",
"Secret": "v1",
}
if os.Getenv("KUBECTL_NODE_BOUND_TOKENS") == "true" {
kinds["Node"] = "v1"
}
return kinds
}
func NewTokenOpts(ioStreams genericiooptions.IOStreams) *TokenOptions {
return &TokenOptions{
PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
}
}
// NewCmdCreateToken returns an initialized Command for 'create token' sub command
func NewCmdCreateToken(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
o := NewTokenOpts(ioStreams)
cmd := &cobra.Command{
Use: "token SERVICE_ACCOUNT_NAME",
DisableFlagsInUseLine: true,
Short: "Request a service account token",
Long: tokenLong,
Example: tokenExample,
ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "serviceaccount"),
Run: func(cmd *cobra.Command, args []string) {
if err := o.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(err)
return
}
if err := o.Validate(); err != nil {
cmdutil.CheckErr(err)
return
}
if err := o.Run(); err != nil {
cmdutil.CheckErr(err)
return
}
},
}
o.PrintFlags.AddFlags(cmd)
cmd.Flags().StringArrayVar(&o.Audiences, "audience", o.Audiences, "Audience of the requested token. If unset, defaults to requesting a token for use with the Kubernetes API server. May be repeated to request a token valid for multiple audiences.")
cmd.Flags().DurationVar(&o.Duration, "duration", o.Duration, "Requested lifetime of the issued token. If not set or if set to 0, the lifetime will be determined by the server automatically. The server may return a token with a longer or shorter lifetime.")
cmd.Flags().StringVar(&o.BoundObjectKind, "bound-object-kind", o.BoundObjectKind, "Kind of an object to bind the token to. "+
"Supported kinds are "+strings.Join(sets.StringKeySet(boundObjectKindToAPIVersions()).List(), ", ")+". "+
"If set, --bound-object-name must be provided.")
cmd.Flags().StringVar(&o.BoundObjectName, "bound-object-name", o.BoundObjectName, "Name of an object to bind the token to. "+
"The token will expire when the object is deleted. "+
"Requires --bound-object-kind.")
cmd.Flags().StringVar(&o.BoundObjectUID, "bound-object-uid", o.BoundObjectUID, "UID of an object to bind the token to. "+
"Requires --bound-object-kind and --bound-object-name. "+
"If unset, the UID of the existing object is used.")
o.Flags = cmd.Flags()
return cmd
}
// Complete completes all the required options
func (o *TokenOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Name, err = NameFromCommandArgs(cmd, args)
if err != nil {
return err
}
o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
client, err := f.KubernetesClientSet()
if err != nil {
return err
}
o.CoreClient = client.CoreV1()
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
return nil
}
// Validate makes sure provided values for TokenOptions are valid
func (o *TokenOptions) Validate() error {
if o.CoreClient == nil {
return fmt.Errorf("no client provided")
}
if len(o.Name) == 0 {
return fmt.Errorf("service account name is required")
}
if len(o.Namespace) == 0 {
return fmt.Errorf("--namespace is required")
}
if o.Duration < 0 {
return fmt.Errorf("--duration must be greater than or equal to 0")
}
if o.Duration%time.Second != 0 {
return fmt.Errorf("--duration cannot be expressed in units less than seconds")
}
for _, aud := range o.Audiences {
if len(aud) == 0 {
return fmt.Errorf("--audience must not be an empty string")
}
}
if len(o.BoundObjectKind) == 0 {
if len(o.BoundObjectName) > 0 {
return fmt.Errorf("--bound-object-name can only be set if --bound-object-kind is provided")
}
if len(o.BoundObjectUID) > 0 {
return fmt.Errorf("--bound-object-uid can only be set if --bound-object-kind is provided")
}
} else {
if _, ok := boundObjectKindToAPIVersions()[o.BoundObjectKind]; !ok {
return fmt.Errorf("supported --bound-object-kind values are %s", strings.Join(sets.StringKeySet(boundObjectKindToAPIVersions()).List(), ", "))
}
if len(o.BoundObjectName) == 0 {
return fmt.Errorf("--bound-object-name is required if --bound-object-kind is provided")
}
}
return nil
}
// Run requests a token
func (o *TokenOptions) Run() error {
request := &authenticationv1.TokenRequest{
Spec: authenticationv1.TokenRequestSpec{
Audiences: o.Audiences,
},
}
if o.Duration > 0 {
request.Spec.ExpirationSeconds = pointer.Int64(int64(o.Duration / time.Second))
}
if len(o.BoundObjectKind) > 0 {
request.Spec.BoundObjectRef = &authenticationv1.BoundObjectReference{
Kind: o.BoundObjectKind,
APIVersion: boundObjectKindToAPIVersions()[o.BoundObjectKind],
Name: o.BoundObjectName,
UID: types.UID(o.BoundObjectUID),
}
}
response, err := o.CoreClient.ServiceAccounts(o.Namespace).CreateToken(context.TODO(), o.Name, request, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create token: %v", err)
}
if len(response.Status.Token) == 0 {
return fmt.Errorf("failed to create token: no token in server response")
}
if o.PrintFlags.OutputFlagSpecified() {
return o.PrintObj(response)
}
if term.IsTerminal(o.Out) {
// include a newline when printing interactively
fmt.Fprintf(o.Out, "%s\n", response.Status.Token)
} else {
// otherwise just print the token
fmt.Fprintf(o.Out, "%s", response.Status.Token)
}
return nil
}

207
vendor/k8s.io/kubectl/pkg/generate/generate.go generated vendored Normal file
View File

@ -0,0 +1,207 @@
/*
Copyright 2018 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 generate
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
)
// GeneratorFunc returns the generators for the provided command
type GeneratorFunc func(cmdName string) map[string]Generator
// GeneratorParam is a parameter for a generator
// TODO: facilitate structured json generator input schemes
type GeneratorParam struct {
Name string
Required bool
}
// Generator is an interface for things that can generate API objects from input
// parameters. One example is the "expose" generator that is capable of exposing
// new replication controllers and services, among other things.
type Generator interface {
// Generate creates an API object given a set of parameters
Generate(params map[string]interface{}) (runtime.Object, error)
// ParamNames returns the list of parameters that this generator uses
ParamNames() []GeneratorParam
}
// StructuredGenerator is an interface for things that can generate API objects not using parameter injection
type StructuredGenerator interface {
// StructuredGenerator creates an API object using pre-configured parameters
StructuredGenerate() (runtime.Object, error)
}
func IsZero(i interface{}) bool {
if i == nil {
return true
}
return reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface())
}
// ValidateParams ensures that all required params are present in the params map
func ValidateParams(paramSpec []GeneratorParam, params map[string]interface{}) error {
allErrs := []error{}
for ix := range paramSpec {
if paramSpec[ix].Required {
value, found := params[paramSpec[ix].Name]
if !found || IsZero(value) {
allErrs = append(allErrs, fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name))
}
}
}
return utilerrors.NewAggregate(allErrs)
}
// AnnotateFlags annotates all flags that are used by generators.
func AnnotateFlags(cmd *cobra.Command, generators map[string]Generator) {
// Iterate over all generators and mark any flags used by them.
for name, generator := range generators {
generatorParams := map[string]struct{}{}
for _, param := range generator.ParamNames() {
generatorParams[param.Name] = struct{}{}
}
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
if _, found := generatorParams[flag.Name]; !found {
// This flag is not used by the current generator
// so skip it.
return
}
if flag.Annotations == nil {
flag.Annotations = map[string][]string{}
}
if annotations := flag.Annotations["generator"]; annotations == nil {
flag.Annotations["generator"] = []string{}
}
flag.Annotations["generator"] = append(flag.Annotations["generator"], name)
})
}
}
// EnsureFlagsValid ensures that no invalid flags are being used against a
func EnsureFlagsValid(cmd *cobra.Command, generators map[string]Generator, generatorInUse string) error {
AnnotateFlags(cmd, generators)
allErrs := []error{}
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
// If the flag hasn't changed, don't validate it.
if !flag.Changed {
return
}
// Look into the flag annotations for the generators that can use it.
if annotations := flag.Annotations["generator"]; len(annotations) > 0 {
annotationMap := map[string]struct{}{}
for _, ann := range annotations {
annotationMap[ann] = struct{}{}
}
// If the current generator is not annotated, then this flag shouldn't
// be used with it.
if _, found := annotationMap[generatorInUse]; !found {
allErrs = append(allErrs, fmt.Errorf("cannot use --%s with --generator=%s", flag.Name, generatorInUse))
}
}
})
return utilerrors.NewAggregate(allErrs)
}
// MakeParams is a utility that creates generator parameters from a command line
func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interface{} {
result := map[string]interface{}{}
for ix := range params {
f := cmd.Flags().Lookup(params[ix].Name)
if f != nil {
result[params[ix].Name] = f.Value.String()
}
}
return result
}
func MakeProtocols(protocols map[string]string) string {
out := []string{}
for key, value := range protocols {
out = append(out, fmt.Sprintf("%s/%s", key, value))
}
return strings.Join(out, ",")
}
func ParseProtocols(protocols interface{}) (map[string]string, error) {
protocolsString, isString := protocols.(string)
if !isString {
return nil, fmt.Errorf("expected string, found %v", protocols)
}
if len(protocolsString) == 0 {
return nil, fmt.Errorf("no protocols passed")
}
portProtocolMap := map[string]string{}
protocolsSlice := strings.Split(protocolsString, ",")
for ix := range protocolsSlice {
portProtocol := strings.Split(protocolsSlice[ix], "/")
if len(portProtocol) != 2 {
return nil, fmt.Errorf("unexpected port protocol mapping: %s", protocolsSlice[ix])
}
if len(portProtocol[0]) == 0 {
return nil, fmt.Errorf("unexpected empty port")
}
if len(portProtocol[1]) == 0 {
return nil, fmt.Errorf("unexpected empty protocol")
}
portProtocolMap[portProtocol[0]] = portProtocol[1]
}
return portProtocolMap, nil
}
// ParseLabels turns a string representation of a label set into a map[string]string
func ParseLabels(labelSpec interface{}) (map[string]string, error) {
labelString, isString := labelSpec.(string)
if !isString {
return nil, fmt.Errorf("expected string, found %v", labelSpec)
}
if len(labelString) == 0 {
return nil, fmt.Errorf("no label spec passed")
}
labels := map[string]string{}
labelSpecs := strings.Split(labelString, ",")
for ix := range labelSpecs {
labelSpec := strings.Split(labelSpecs[ix], "=")
if len(labelSpec) != 2 {
return nil, fmt.Errorf("unexpected label spec: %s", labelSpecs[ix])
}
if len(labelSpec[0]) == 0 {
return nil, fmt.Errorf("unexpected empty label key")
}
labels[labelSpec[0]] = labelSpec[1]
}
return labels, nil
}
func GetBool(params map[string]string, key string, defValue bool) (bool, error) {
if val, found := params[key]; !found {
return defValue, nil
} else {
return strconv.ParseBool(val)
}
}

130
vendor/k8s.io/kubectl/pkg/util/hash/hash.go generated vendored Normal file
View File

@ -0,0 +1,130 @@
/*
Copyright 2017 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 hash
import (
"crypto/sha256"
"encoding/json"
"fmt"
"k8s.io/api/core/v1"
)
// ConfigMapHash returns a hash of the ConfigMap.
// The Data, Kind, and Name are taken into account.
func ConfigMapHash(cm *v1.ConfigMap) (string, error) {
encoded, err := encodeConfigMap(cm)
if err != nil {
return "", err
}
h, err := encodeHash(hash(encoded))
if err != nil {
return "", err
}
return h, nil
}
// SecretHash returns a hash of the Secret.
// The Data, Kind, Name, and Type are taken into account.
func SecretHash(sec *v1.Secret) (string, error) {
encoded, err := encodeSecret(sec)
if err != nil {
return "", err
}
h, err := encodeHash(hash(encoded))
if err != nil {
return "", err
}
return h, nil
}
// encodeConfigMap encodes a ConfigMap.
// Data, Kind, and Name are taken into account.
func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
// json.Marshal sorts the keys in a stable order in the encoding
m := map[string]interface{}{
"kind": "ConfigMap",
"name": cm.Name,
"data": cm.Data,
}
if cm.Immutable != nil {
m["immutable"] = *cm.Immutable
}
if len(cm.BinaryData) > 0 {
m["binaryData"] = cm.BinaryData
}
data, err := json.Marshal(m)
if err != nil {
return "", err
}
return string(data), nil
}
// encodeSecret encodes a Secret.
// Data, Kind, Name, and Type are taken into account.
func encodeSecret(sec *v1.Secret) (string, error) {
m := map[string]interface{}{
"kind": "Secret",
"type": sec.Type,
"name": sec.Name,
"data": sec.Data,
}
if sec.Immutable != nil {
m["immutable"] = *sec.Immutable
}
// json.Marshal sorts the keys in a stable order in the encoding
data, err := json.Marshal(m)
if err != nil {
return "", err
}
return string(data), nil
}
// encodeHash extracts the first 40 bits of the hash from the hex string
// (1 hex char represents 4 bits), and then maps vowels and vowel-like hex
// characters to consonants to prevent bad words from being formed (the theory
// is that no vowels makes it really hard to make bad words). Since the string
// is hex, the only vowels it can contain are 'a' and 'e'.
// We picked some arbitrary consonants to map to from the same character set as GenerateName.
// See: https://github.com/kubernetes/apimachinery/blob/dc1f89aff9a7509782bde3b68824c8043a3e58cc/pkg/util/rand/rand.go#L75
// If the hex string contains fewer than ten characters, returns an error.
func encodeHash(hex string) (string, error) {
if len(hex) < 10 {
return "", fmt.Errorf("the hex string must contain at least 10 characters")
}
enc := []rune(hex[:10])
for i := range enc {
switch enc[i] {
case '0':
enc[i] = 'g'
case '1':
enc[i] = 'h'
case '3':
enc[i] = 'k'
case 'a':
enc[i] = 'm'
case 'e':
enc[i] = 't'
}
}
return string(enc), nil
}
// hash hashes `data` with sha256 and returns the hex string
func hash(data string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
}

3
vendor/modules.txt vendored
View File

@ -1682,6 +1682,7 @@ k8s.io/kube-openapi/pkg/validation/strfmt/bson
k8s.io/kubectl/pkg/apps
k8s.io/kubectl/pkg/cmd/apiresources
k8s.io/kubectl/pkg/cmd/apply
k8s.io/kubectl/pkg/cmd/create
k8s.io/kubectl/pkg/cmd/delete
k8s.io/kubectl/pkg/cmd/describe
k8s.io/kubectl/pkg/cmd/exec
@ -1694,6 +1695,7 @@ k8s.io/kubectl/pkg/cmd/util/editor/crlf
k8s.io/kubectl/pkg/cmd/util/podcmd
k8s.io/kubectl/pkg/cmd/wait
k8s.io/kubectl/pkg/describe
k8s.io/kubectl/pkg/generate
k8s.io/kubectl/pkg/metricsutil
k8s.io/kubectl/pkg/polymorphichelpers
k8s.io/kubectl/pkg/rawhttp
@ -1704,6 +1706,7 @@ k8s.io/kubectl/pkg/util/completion
k8s.io/kubectl/pkg/util/deployment
k8s.io/kubectl/pkg/util/event
k8s.io/kubectl/pkg/util/fieldpath
k8s.io/kubectl/pkg/util/hash
k8s.io/kubectl/pkg/util/i18n
k8s.io/kubectl/pkg/util/interrupt
k8s.io/kubectl/pkg/util/openapi