2015-01-12 23:30:52 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2015-01-12 23:30:52 +00:00
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 cmd
import (
"fmt"
"io"
2015-05-21 20:53:10 +00:00
"os"
2015-08-04 19:54:17 +00:00
"time"
2015-01-12 23:30:52 +00:00
2015-08-05 22:05:17 +00:00
"github.com/spf13/cobra"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2015-10-20 18:27:26 +00:00
"k8s.io/kubernetes/pkg/api/meta"
2015-08-13 19:01:50 +00:00
client "k8s.io/kubernetes/pkg/client/unversioned"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2015-08-21 02:35:28 +00:00
"k8s.io/kubernetes/pkg/kubectl/resource"
2015-10-20 18:27:26 +00:00
"k8s.io/kubernetes/pkg/runtime"
2015-01-12 23:30:52 +00:00
)
2015-02-20 21:28:43 +00:00
const (
run_long = ` Create and run a particular image , possibly replicated .
Creates a replication controller to manage the created container ( s ) . `
2015-09-02 09:56:29 +00:00
run_example = ` # Start a single instance of nginx .
2015-05-21 20:53:10 +00:00
$ kubectl run nginx -- image = nginx
2015-01-12 23:30:52 +00:00
2015-09-01 03:03:29 +00:00
# Start a single instance of hazelcast and let the container expose port 5701 .
$ kubectl run hazelcast -- image = hazelcast -- port = 5701
# Start a single instance of hazelcast and set environment variables "DNS_DOMAIN=cluster" and "POD_NAMESPACE=default" in the container .
2015-09-28 05:58:31 +00:00
$ kubectl run hazelcast -- image = hazelcast -- env = "DNS_DOMAIN=cluster" -- env = "POD_NAMESPACE=default"
2015-09-01 03:03:29 +00:00
2015-09-02 09:56:29 +00:00
# Start a replicated instance of nginx .
2015-05-21 20:53:10 +00:00
$ kubectl run nginx -- image = nginx -- replicas = 5
2015-01-12 23:30:52 +00:00
2015-08-12 16:50:09 +00:00
# Dry run . Print the corresponding API objects without creating them .
2015-05-21 20:53:10 +00:00
$ kubectl run nginx -- image = nginx -- dry - run
2015-01-15 00:58:12 +00:00
2015-08-12 16:50:09 +00:00
# Start a single instance of nginx , but overload the spec of the replication controller with a partial set of values parsed from JSON .
2015-08-12 05:48:00 +00:00
$ kubectl run nginx -- image = nginx -- overrides = ' { "apiVersion" : "v1" , "spec" : { ... } } '
# Start a single instance of nginx and keep it in the foreground , don ' t restart it if it exits .
2015-10-30 22:40:05 +00:00
$ kubectl run - i -- tty nginx -- image = nginx -- restart = Never
2015-08-12 05:48:00 +00:00
# Start the nginx container using the default command , but use custom arguments ( arg1 . . argN ) for that command .
$ kubectl run nginx -- image = nginx -- < arg1 > < arg2 > ... < argN >
# Start the nginx container using a different command and custom arguments
$ kubectl run nginx -- image = nginx -- command -- < cmd > < arg1 > ... < argN > `
2015-02-20 21:28:43 +00:00
)
2015-02-03 17:59:21 +00:00
2015-08-04 19:54:17 +00:00
func NewCmdRun ( f * cmdutil . Factory , cmdIn io . Reader , cmdOut , cmdErr io . Writer ) * cobra . Command {
2015-02-20 21:28:43 +00:00
cmd := & cobra . Command {
2015-09-14 12:18:42 +00:00
Use : "run NAME --image=image [--env=\"key=value\"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]" ,
2015-05-21 20:53:10 +00:00
// run-container is deprecated
Aliases : [ ] string { "run-container" } ,
2015-02-20 21:28:43 +00:00
Short : "Run a particular image on the cluster." ,
Long : run_long ,
Example : run_example ,
2015-01-12 23:30:52 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2015-09-14 12:18:42 +00:00
argsLenAtDash := cmd . ArgsLenAtDash ( )
err := Run ( f , cmdIn , cmdOut , cmdErr , cmd , args , argsLenAtDash )
2015-04-07 18:21:25 +00:00
cmdutil . CheckErr ( err )
2015-01-12 23:30:52 +00:00
} ,
}
2015-04-07 18:21:25 +00:00
cmdutil . AddPrinterFlags ( cmd )
2015-10-20 18:27:26 +00:00
addRunFlags ( cmd )
2015-11-04 21:47:08 +00:00
cmdutil . AddApplyAnnotationFlags ( cmd )
2015-10-20 18:27:26 +00:00
return cmd
}
func addRunFlags ( cmd * cobra . Command ) {
2015-08-04 19:54:17 +00:00
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'." )
2015-02-03 17:59:21 +00:00
cmd . Flags ( ) . String ( "image" , "" , "The image for the container to run." )
2015-03-17 15:49:35 +00:00
cmd . MarkFlagRequired ( "image" )
2015-02-03 17:59:21 +00:00
cmd . Flags ( ) . IntP ( "replicas" , "r" , 1 , "Number of replicas to create for this container. Default is 1." )
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." )
2015-09-01 03:03:29 +00:00
cmd . Flags ( ) . StringSlice ( "env" , [ ] string { } , "Environment variables to set in the container" )
2015-10-20 18:27:26 +00:00
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." )
2015-04-29 23:54:23 +00:00
cmd . Flags ( ) . Int ( "hostport" , - 1 , "The host port mapping for the container port. To demonstrate a single-machine container." )
2015-05-21 20:53:10 +00:00
cmd . Flags ( ) . StringP ( "labels" , "l" , "" , "Labels to apply to the pod(s)." )
2015-08-04 19:54:17 +00:00
cmd . Flags ( ) . BoolP ( "stdin" , "i" , false , "Keep stdin open on the container(s) in the pod, even if nothing is attached." )
cmd . Flags ( ) . Bool ( "tty" , false , "Allocated a TTY for each container in the pod. Because -t is currently shorthand for --template, -t is not supported for --tty. This shorthand is deprecated and we expect to adopt -t for --tty soon." )
cmd . Flags ( ) . Bool ( "attach" , false , "If true, wait for the Pod to start running, and then attach to the Pod as if 'kubectl attach ...' were called. Default false, unless '-i/--interactive' is set, in which case the default is true." )
2015-10-06 15:31:48 +00:00
cmd . Flags ( ) . Bool ( "leave-stdin-open" , false , "If the pod is started in interactive mode or with stdin, leave stdin open after the first attach completes. By default, stdin will be closed after the first attach completes." )
2015-08-04 19:54:17 +00:00
cmd . Flags ( ) . String ( "restart" , "Always" , "The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and --replicas must be 1. Default 'Always'" )
2015-08-12 05:48:00 +00:00
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." )
2015-09-09 21:22:56 +00:00
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'" )
2015-10-20 18:27:26 +00:00
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." )
2015-01-12 23:30:52 +00:00
}
2015-03-09 22:08:16 +00:00
2015-09-14 12:18:42 +00:00
func Run ( f * cmdutil . Factory , cmdIn io . Reader , cmdOut , cmdErr io . Writer , cmd * cobra . Command , args [ ] string , argsLenAtDash int ) error {
2015-07-09 23:15:42 +00:00
if len ( os . Args ) > 1 && os . Args [ 1 ] == "run-container" {
2015-05-21 20:53:10 +00:00
printDeprecationWarning ( "run" , "run-container" )
}
2015-09-14 12:18:42 +00:00
// Let kubectl run follow rules for `--`, see #13004 issue
if len ( args ) == 0 || argsLenAtDash == 0 {
2015-05-21 20:53:10 +00:00
return cmdutil . UsageError ( cmd , "NAME is required for run" )
2015-03-09 22:08:16 +00:00
}
2015-08-04 19:54:17 +00:00
interactive := cmdutil . GetFlagBool ( cmd , "stdin" )
tty := cmdutil . GetFlagBool ( cmd , "tty" )
if tty && ! interactive {
return cmdutil . UsageError ( cmd , "-i/--stdin is required for containers with --tty=true" )
}
replicas := cmdutil . GetFlagInt ( cmd , "replicas" )
if interactive && replicas != 1 {
return cmdutil . UsageError ( cmd , fmt . Sprintf ( "-i/--stdin requires that replicas is 1, found %d" , replicas ) )
}
2015-06-26 20:49:34 +00:00
namespace , _ , err := f . DefaultNamespace ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2015-08-04 19:54:17 +00:00
restartPolicy , err := getRestartPolicy ( cmd , interactive )
if err != nil {
return err
}
if restartPolicy != api . RestartPolicyAlways && replicas != 1 {
2015-10-06 15:31:48 +00:00
return cmdutil . UsageError ( cmd , fmt . Sprintf ( "--restart=%s requires that --replicas=1, found %d" , restartPolicy , replicas ) )
2015-08-04 19:54:17 +00:00
}
2015-10-20 18:27:26 +00:00
2015-04-07 18:21:25 +00:00
generatorName := cmdutil . GetFlagString ( cmd , "generator" )
2015-08-04 19:54:17 +00:00
if len ( generatorName ) == 0 {
if restartPolicy == api . RestartPolicyAlways {
generatorName = "run/v1"
} else {
generatorName = "run-pod/v1"
}
}
2015-05-05 09:15:01 +00:00
generator , found := f . Generator ( generatorName )
2015-03-09 22:08:16 +00:00
if ! found {
2015-08-04 19:54:17 +00:00
return cmdutil . UsageError ( cmd , fmt . Sprintf ( "Generator: %s not found." , generatorName ) )
2015-03-09 22:08:16 +00:00
}
names := generator . ParamNames ( )
params := kubectl . MakeParams ( cmd , names )
params [ "name" ] = args [ 0 ]
2015-08-12 05:48:00 +00:00
if len ( args ) > 1 {
params [ "args" ] = args [ 1 : ]
}
2015-09-01 03:03:29 +00:00
params [ "env" ] = cmdutil . GetFlagStringSlice ( cmd , "env" )
2015-10-20 18:27:26 +00:00
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 {
2015-03-09 22:08:16 +00:00
return err
}
}
2015-10-22 17:34:19 +00:00
obj , _ , mapper , mapping , err := createGeneratedObject ( f , cmd , generator , names , params , cmdutil . GetFlagString ( cmd , "overrides" ) , namespace )
2015-08-21 02:35:28 +00:00
if err != nil {
return err
}
2015-08-04 19:54:17 +00:00
attachFlag := cmd . Flags ( ) . Lookup ( "attach" )
attach := cmdutil . GetFlagBool ( cmd , "attach" )
if ! attachFlag . Changed && interactive {
attach = true
}
if attach {
opts := & AttachOptions {
In : cmdIn ,
Out : cmdOut ,
Err : cmdErr ,
Stdin : interactive ,
TTY : tty ,
Attach : & DefaultRemoteAttach { } ,
}
config , err := f . ClientConfig ( )
if err != nil {
return err
}
opts . Config = config
client , err := f . Client ( )
if err != nil {
return err
}
opts . Client = client
2015-10-22 17:34:19 +00:00
attachablePod , err := f . AttachablePodForObject ( obj )
if err != nil {
return err
2015-08-04 19:54:17 +00:00
}
2015-10-18 14:20:28 +00:00
return handleAttachPod ( f , client , attachablePod , opts )
2015-08-04 19:54:17 +00:00
}
2015-09-01 13:30:15 +00:00
outputFormat := cmdutil . GetFlagString ( cmd , "output" )
if outputFormat != "" {
return f . PrintObject ( cmd , obj , cmdOut )
}
cmdutil . PrintSuccess ( mapper , false , cmdOut , mapping . Resource , args [ 0 ] , "created" )
return nil
2015-08-04 19:54:17 +00:00
}
2015-09-02 03:54:10 +00:00
func waitForPodRunning ( c * client . Client , pod * api . Pod , out io . Writer ) ( status api . PodPhase , err error ) {
2015-08-04 19:54:17 +00:00
for {
pod , err := c . Pods ( pod . Namespace ) . Get ( pod . Name )
if err != nil {
2015-09-02 03:54:10 +00:00
return api . PodUnknown , err
2015-08-04 19:54:17 +00:00
}
2015-09-02 03:54:10 +00:00
ready := false
2015-08-04 19:54:17 +00:00
if pod . Status . Phase == api . PodRunning {
2015-09-02 03:54:10 +00:00
ready = true
2015-08-04 19:54:17 +00:00
for _ , status := range pod . Status . ContainerStatuses {
if ! status . Ready {
ready = false
break
}
}
if ready {
2015-09-02 03:54:10 +00:00
return api . PodRunning , nil
2015-08-04 19:54:17 +00:00
}
}
2015-09-02 03:54:10 +00:00
if pod . Status . Phase == api . PodSucceeded || pod . Status . Phase == api . PodFailed {
return pod . Status . Phase , nil
}
fmt . Fprintf ( out , "Waiting for pod %s/%s to be running, status is %s, pod ready: %v\n" , pod . Namespace , pod . Name , pod . Status . Phase , ready )
2015-08-04 19:54:17 +00:00
time . Sleep ( 2 * time . Second )
continue
}
}
2015-10-18 14:20:28 +00:00
func handleAttachPod ( f * cmdutil . Factory , c * client . Client , pod * api . Pod , opts * AttachOptions ) error {
2015-09-02 03:54:10 +00:00
status , err := waitForPodRunning ( c , pod , opts . Out )
if err != nil {
2015-08-04 19:54:17 +00:00
return err
}
2015-09-02 03:54:10 +00:00
if status == api . PodSucceeded || status == api . PodFailed {
2015-10-18 14:20:28 +00:00
req , err := f . LogsForObject ( pod , & api . PodLogOptions { Container : opts . GetContainerName ( pod ) } )
if err != nil {
return err
}
readCloser , err := req . Stream ( )
if err != nil {
return err
}
defer readCloser . Close ( )
_ , err = io . Copy ( opts . Out , readCloser )
return err
2015-09-02 03:54:10 +00:00
}
2015-08-04 19:54:17 +00:00
opts . Client = c
opts . PodName = pod . Name
opts . Namespace = pod . Namespace
2015-09-29 19:43:15 +00:00
if err := opts . Run ( ) ; err != nil {
fmt . Fprintf ( opts . Out , "Error attaching, falling back to logs: %v\n" , err )
2015-10-18 14:20:28 +00:00
req , err := f . LogsForObject ( pod , & api . PodLogOptions { Container : opts . GetContainerName ( pod ) } )
if err != nil {
return err
}
readCloser , err := req . Stream ( )
if err != nil {
return err
}
defer readCloser . Close ( )
_ , err = io . Copy ( opts . Out , readCloser )
return err
2015-09-29 19:43:15 +00:00
}
return nil
2015-03-09 22:08:16 +00:00
}
2015-08-04 19:54:17 +00:00
func getRestartPolicy ( cmd * cobra . Command , interactive bool ) ( api . RestartPolicy , error ) {
restart := cmdutil . GetFlagString ( cmd , "restart" )
if len ( restart ) == 0 {
if interactive {
return api . RestartPolicyOnFailure , nil
} else {
return api . RestartPolicyAlways , nil
}
}
switch api . RestartPolicy ( restart ) {
case api . RestartPolicyAlways :
return api . RestartPolicyAlways , nil
case api . RestartPolicyOnFailure :
return api . RestartPolicyOnFailure , nil
case api . RestartPolicyNever :
return api . RestartPolicyNever , nil
default :
return "" , cmdutil . UsageError ( cmd , fmt . Sprintf ( "invalid restart policy: %s" , restart ) )
}
}
2015-10-20 18:27:26 +00:00
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
}
2015-11-04 21:47:08 +00:00
if err := kubectl . CreateOrUpdateAnnotation ( cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag ) , info ) ; err != nil {
2015-10-20 18:27:26 +00:00
return nil , "" , nil , nil , err
}
2015-10-28 16:43:21 +00:00
obj , err = resource . NewHelper ( client , mapping ) . Create ( namespace , false , info . Object )
2015-10-20 18:27:26 +00:00
if err != nil {
return nil , "" , nil , nil , err
}
}
return obj , kind , mapper , mapping , err
}