Merge pull request #15921 from brendandburns/fix2

Add a --expose flag to kubectl run
This commit is contained in:
Jeff Lowdermilk
2015-10-21 13:17:26 -07:00
6 changed files with 314 additions and 56 deletions

View File

@@ -24,12 +24,14 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
)
const (
@@ -77,6 +79,11 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
},
}
cmdutil.AddPrinterFlags(cmd)
addRunFlags(cmd)
return cmd
}
func addRunFlags(cmd *cobra.Command) {
cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'run/v1' if --restart=Always, otherwise the default is 'run-pod/v1'.")
cmd.Flags().String("image", "", "The image for the container to run.")
cmd.MarkFlagRequired("image")
@@ -84,7 +91,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
cmd.Flags().String("overrides", "", "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.")
cmd.Flags().StringSlice("env", []string{}, "Environment variables to set in the container")
cmd.Flags().Int("port", -1, "The port that this container exposes.")
cmd.Flags().Int("port", -1, "The port that this container exposes. If --expose is true, this is also the port used by the service that is created.")
cmd.Flags().Int("hostport", -1, "The host port mapping for the container port. To demonstrate a single-machine container.")
cmd.Flags().StringP("labels", "l", "", "Labels to apply to the pod(s).")
cmd.Flags().BoolP("stdin", "i", false, "Keep stdin open on the container(s) in the pod, even if nothing is attached.")
@@ -95,7 +102,9 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
cmd.Flags().Bool("command", false, "If true and extra arguments are present, use them as the 'command' field in the container, rather than the 'args' field which is the default.")
cmd.Flags().String("requests", "", "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'")
cmd.Flags().String("limits", "", "The resource requirement limits for this container. For example, 'cpu=200m,memory=512Mi'")
return cmd
cmd.Flags().Bool("expose", false, "If true, a public, external service is created for the container(s) which are run")
cmd.Flags().String("service-generator", "service/v2", "The name of the generator to use for creating a service. Only used if --expose is true")
cmd.Flags().String("service-overrides", "", "An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true.")
}
func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string) error {
@@ -129,6 +138,7 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
if restartPolicy != api.RestartPolicyAlways && replicas != 1 {
return cmdutil.UsageError(cmd, fmt.Sprintf("--restart=%s requires that --replicas=1, found %d", restartPolicy, replicas))
}
generatorName := cmdutil.GetFlagString(cmd, "generator")
if len(generatorName) == 0 {
if restartPolicy == api.RestartPolicyAlways {
@@ -150,64 +160,20 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
params["env"] = cmdutil.GetFlagStringSlice(cmd, "env")
err = kubectl.ValidateParams(names, params)
if err != nil {
return err
}
obj, err := generator.Generate(params)
if err != nil {
return err
}
mapper, typer := f.Object()
version, kind, err := typer.ObjectVersionAndKind(obj)
if err != nil {
return err
}
inline := cmdutil.GetFlagString(cmd, "overrides")
if len(inline) > 0 {
obj, err = cmdutil.Merge(obj, inline, kind)
if err != nil {
if cmdutil.GetFlagBool(cmd, "expose") {
serviceGenerator := cmdutil.GetFlagString(cmd, "service-generator")
if len(serviceGenerator) == 0 {
return cmdutil.UsageError(cmd, fmt.Sprintf("No service generator specified"))
}
if err := generateService(f, cmd, args, serviceGenerator, params, namespace, cmdOut); err != nil {
return err
}
}
mapping, err := mapper.RESTMapping(kind, version)
obj, kind, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "overrides"), namespace)
if err != nil {
return err
}
client, err := f.RESTClient(mapping)
if err != nil {
return err
}
// TODO: extract this flag to a central location, when such a location exists.
if !cmdutil.GetFlagBool(cmd, "dry-run") {
resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()}
info, err := resourceMapper.InfoForObject(obj)
if err != nil {
return err
}
// Serialize the configuration into an annotation.
if err := kubectl.UpdateApplyAnnotation(info); err != nil {
return err
}
// Serialize the object with the annotation applied.
data, err := mapping.Codec.Encode(info.Object)
if err != nil {
return err
}
obj, err = resource.NewHelper(client, mapping).Create(namespace, false, data)
if err != nil {
return err
}
}
attachFlag := cmd.Flags().Lookup("attach")
attach := cmdutil.GetFlagBool(cmd, "attach")
@@ -337,3 +303,110 @@ func getRestartPolicy(cmd *cobra.Command, interactive bool) (api.RestartPolicy,
return "", cmdutil.UsageError(cmd, fmt.Sprintf("invalid restart policy: %s", restart))
}
}
func generateService(f *cmdutil.Factory, cmd *cobra.Command, args []string, serviceGenerator string, paramsIn map[string]interface{}, namespace string, out io.Writer) error {
generator, found := f.Generator(serviceGenerator)
if !found {
return fmt.Errorf("missing service generator: %s", serviceGenerator)
}
names := generator.ParamNames()
port := cmdutil.GetFlagInt(cmd, "port")
if port < 1 {
return fmt.Errorf("--port must be a positive integer when exposing a service")
}
params := map[string]interface{}{}
for key, value := range paramsIn {
_, isString := value.(string)
if isString {
params[key] = value
}
}
name, found := params["name"]
if !found || len(name.(string)) == 0 {
return fmt.Errorf("name is a required parameter")
}
selector, found := params["labels"]
if !found || len(selector.(string)) == 0 {
selector = fmt.Sprintf("run=%s", name.(string))
}
params["selector"] = selector
if defaultName, found := params["default-name"]; !found || len(defaultName.(string)) == 0 {
params["default-name"] = name
}
obj, _, mapper, mapping, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "service-overrides"), namespace)
if err != nil {
return err
}
if cmdutil.GetFlagString(cmd, "output") != "" {
return f.PrintObject(cmd, obj, out)
}
cmdutil.PrintSuccess(mapper, false, out, mapping.Resource, args[0], "created")
return nil
}
func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kubectl.Generator, names []kubectl.GeneratorParam, params map[string]interface{}, overrides, namespace string) (runtime.Object, string, meta.RESTMapper, *meta.RESTMapping, error) {
err := kubectl.ValidateParams(names, params)
if err != nil {
return nil, "", nil, nil, err
}
obj, err := generator.Generate(params)
if err != nil {
return nil, "", nil, nil, err
}
mapper, typer := f.Object()
version, kind, err := typer.ObjectVersionAndKind(obj)
if err != nil {
return nil, "", nil, nil, err
}
if len(overrides) > 0 {
obj, err = cmdutil.Merge(obj, overrides, kind)
if err != nil {
return nil, "", nil, nil, err
}
}
mapping, err := mapper.RESTMapping(kind, version)
if err != nil {
return nil, "", nil, nil, err
}
client, err := f.RESTClient(mapping)
if err != nil {
return nil, "", nil, nil, err
}
// TODO: extract this flag to a central location, when such a location exists.
if !cmdutil.GetFlagBool(cmd, "dry-run") {
resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()}
info, err := resourceMapper.InfoForObject(obj)
if err != nil {
return nil, "", nil, nil, err
}
// Serialize the configuration into an annotation.
if err := kubectl.UpdateApplyAnnotation(info); err != nil {
return nil, "", nil, nil, err
}
// Serialize the object with the annotation applied.
data, err := mapping.Codec.Encode(info.Object)
if err != nil {
return nil, "", nil, nil, err
}
obj, err = resource.NewHelper(client, mapping).Create(namespace, false, data)
if err != nil {
return nil, "", nil, nil, err
}
}
return obj, kind, mapper, mapping, err
}