mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
Added job/v1 generator to kubectl run
This commit is contained in:
parent
aca37830b1
commit
de83631768
@ -40,7 +40,7 @@ Creates a deployment or job to manage the created container(s).
|
|||||||
|
|
||||||
.PP
|
.PP
|
||||||
\fB\-\-generator\fP=""
|
\fB\-\-generator\fP=""
|
||||||
The name of the API generator to use. Default is 'deployment/v1beta1' if \-\-restart=Always, otherwise the default is 'job/v1beta1'.
|
The name of the API generator to use. Default is 'deployment/v1beta1' if \-\-restart=Always, otherwise the default is 'job/v1'.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
\fB\-\-hostport\fP=\-1
|
\fB\-\-hostport\fP=\-1
|
||||||
|
@ -88,7 +88,7 @@ kubectl run pi --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print
|
|||||||
--dry-run[=false]: If true, only print the object that would be sent, without sending it.
|
--dry-run[=false]: If true, only print the object that would be sent, without sending it.
|
||||||
--env=[]: Environment variables to set in the container
|
--env=[]: Environment variables to set in the container
|
||||||
--expose[=false]: If true, a public, external service is created for the container(s) which are run
|
--expose[=false]: If true, a public, external service is created for the container(s) which are run
|
||||||
--generator="": The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, otherwise the default is 'job/v1beta1'.
|
--generator="": The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, otherwise the default is 'job/v1'.
|
||||||
--hostport=-1: The host port mapping for the container port. To demonstrate a single-machine container.
|
--hostport=-1: The host port mapping for the container port. To demonstrate a single-machine container.
|
||||||
--image="": The image for the container to run.
|
--image="": The image for the container to run.
|
||||||
-l, --labels="": Labels to apply to the pod(s).
|
-l, --labels="": Labels to apply to the pod(s).
|
||||||
|
@ -755,6 +755,12 @@ __EOF__
|
|||||||
kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" 'pi:'
|
kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" 'pi:'
|
||||||
# Clean up
|
# Clean up
|
||||||
kubectl delete jobs pi "${kube_flags[@]}"
|
kubectl delete jobs pi "${kube_flags[@]}"
|
||||||
|
# Command
|
||||||
|
kubectl run pi --generator=job/v1 --image=perl --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(20)' "${kube_flags[@]}"
|
||||||
|
# Post-Condition: Job "pi" is created
|
||||||
|
kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" 'pi:'
|
||||||
|
# Clean up
|
||||||
|
kubectl delete jobs pi "${kube_flags[@]}"
|
||||||
# Post-condition: no pods exist.
|
# Post-condition: no pods exist.
|
||||||
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
|
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
|
||||||
# Pre-Condition: no Deployment exists
|
# Pre-Condition: no Deployment exists
|
||||||
|
@ -88,7 +88,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addRunFlags(cmd *cobra.Command) {
|
func addRunFlags(cmd *cobra.Command) {
|
||||||
cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, otherwise the default is 'job/v1beta1'.")
|
cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, otherwise the default is 'job/v1'.")
|
||||||
cmd.Flags().String("image", "", "The image for the container to run.")
|
cmd.Flags().String("image", "", "The image for the container to run.")
|
||||||
cmd.MarkFlagRequired("image")
|
cmd.MarkFlagRequired("image")
|
||||||
cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.")
|
cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.")
|
||||||
@ -149,7 +149,7 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
|
|||||||
if restartPolicy == api.RestartPolicyAlways {
|
if restartPolicy == api.RestartPolicyAlways {
|
||||||
generatorName = "deployment/v1beta1"
|
generatorName = "deployment/v1beta1"
|
||||||
} else {
|
} else {
|
||||||
generatorName = "job/v1beta1"
|
generatorName = "job/v1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generators := f.Generators("run")
|
generators := f.Generators("run")
|
||||||
|
@ -143,6 +143,7 @@ const (
|
|||||||
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
|
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
|
||||||
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
||||||
JobV1Beta1GeneratorName = "job/v1beta1"
|
JobV1Beta1GeneratorName = "job/v1beta1"
|
||||||
|
JobV1GeneratorName = "job/v1"
|
||||||
NamespaceV1GeneratorName = "namespace/v1"
|
NamespaceV1GeneratorName = "namespace/v1"
|
||||||
SecretV1GeneratorName = "secret/v1"
|
SecretV1GeneratorName = "secret/v1"
|
||||||
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
||||||
@ -161,6 +162,7 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
|
|||||||
RunPodV1GeneratorName: kubectl.BasicPod{},
|
RunPodV1GeneratorName: kubectl.BasicPod{},
|
||||||
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
|
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
|
||||||
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
|
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
|
||||||
|
JobV1GeneratorName: kubectl.JobV1{},
|
||||||
}
|
}
|
||||||
generators["autoscale"] = map[string]kubectl.Generator{
|
generators["autoscale"] = map[string]kubectl.Generator{
|
||||||
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
|
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
|
||||||
|
@ -24,6 +24,8 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/util/validation"
|
"k8s.io/kubernetes/pkg/util/validation"
|
||||||
@ -187,6 +189,24 @@ func getEnvs(genericParams map[string]interface{}) ([]api.EnvVar, error) {
|
|||||||
return envs, nil
|
return envs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getV1Envs(genericParams map[string]interface{}) ([]v1.EnvVar, error) {
|
||||||
|
var envs []v1.EnvVar
|
||||||
|
envStrings, found := genericParams["env"]
|
||||||
|
if found {
|
||||||
|
if envStringArray, isArray := envStrings.([]string); isArray {
|
||||||
|
var err error
|
||||||
|
envs, err = parseV1Envs(envStringArray)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
delete(genericParams, "env")
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("expected []string, found: %v", envStrings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envs, nil
|
||||||
|
}
|
||||||
|
|
||||||
type JobV1Beta1 struct{}
|
type JobV1Beta1 struct{}
|
||||||
|
|
||||||
func (JobV1Beta1) ParamNames() []GeneratorParam {
|
func (JobV1Beta1) ParamNames() []GeneratorParam {
|
||||||
@ -256,7 +276,7 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
|
|||||||
|
|
||||||
restartPolicy := api.RestartPolicy(params["restart"])
|
restartPolicy := api.RestartPolicy(params["restart"])
|
||||||
if len(restartPolicy) == 0 {
|
if len(restartPolicy) == 0 {
|
||||||
restartPolicy = api.RestartPolicyAlways
|
restartPolicy = api.RestartPolicyNever
|
||||||
}
|
}
|
||||||
podSpec.RestartPolicy = restartPolicy
|
podSpec.RestartPolicy = restartPolicy
|
||||||
|
|
||||||
@ -282,6 +302,97 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
|
|||||||
return &job, nil
|
return &job, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JobV1 struct{}
|
||||||
|
|
||||||
|
func (JobV1) ParamNames() []GeneratorParam {
|
||||||
|
return []GeneratorParam{
|
||||||
|
{"labels", false},
|
||||||
|
{"default-name", false},
|
||||||
|
{"name", true},
|
||||||
|
{"image", true},
|
||||||
|
{"port", false},
|
||||||
|
{"hostport", false},
|
||||||
|
{"stdin", false},
|
||||||
|
{"leave-stdin-open", false},
|
||||||
|
{"tty", false},
|
||||||
|
{"command", false},
|
||||||
|
{"args", false},
|
||||||
|
{"env", false},
|
||||||
|
{"requests", false},
|
||||||
|
{"limits", false},
|
||||||
|
{"restart", false},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||||
|
args, err := getArgs(genericParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
envs, err := getV1Envs(genericParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := getParams(genericParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := getName(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
labels, err := getLabels(params, true, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
podSpec, err := makeV1PodSpec(params, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = updateV1PodContainers(params, args, envs, podSpec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin
|
||||||
|
|
||||||
|
if err := updateV1PodPorts(params, podSpec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
restartPolicy := v1.RestartPolicy(params["restart"])
|
||||||
|
if len(restartPolicy) == 0 {
|
||||||
|
restartPolicy = v1.RestartPolicyNever
|
||||||
|
}
|
||||||
|
podSpec.RestartPolicy = restartPolicy
|
||||||
|
|
||||||
|
job := batchv1.Job{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
|
Spec: batchv1.JobSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
|
Spec: *podSpec,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &job, nil
|
||||||
|
}
|
||||||
|
|
||||||
type BasicReplicationController struct{}
|
type BasicReplicationController struct{}
|
||||||
|
|
||||||
func (BasicReplicationController) ParamNames() []GeneratorParam {
|
func (BasicReplicationController) ParamNames() []GeneratorParam {
|
||||||
@ -327,6 +438,30 @@ func populateResourceList(spec string) (api.ResourceList, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
||||||
|
func populateV1ResourceList(spec string) (v1.ResourceList, error) {
|
||||||
|
// empty input gets a nil response to preserve generator test expected behaviors
|
||||||
|
if spec == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := v1.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 := v1.ResourceName(parts[0])
|
||||||
|
resourceQuantity, err := resource.ParseQuantity(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result[resourceName] = *resourceQuantity
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// HandleResourceRequirements parses the limits and requests parameters if specified
|
// HandleResourceRequirements parses the limits and requests parameters if specified
|
||||||
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
|
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
|
||||||
result := api.ResourceRequirements{}
|
result := api.ResourceRequirements{}
|
||||||
@ -343,6 +478,22 @@ func HandleResourceRequirements(params map[string]string) (api.ResourceRequireme
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleResourceRequirements parses the limits and requests parameters if specified
|
||||||
|
func handleV1ResourceRequirements(params map[string]string) (v1.ResourceRequirements, error) {
|
||||||
|
result := v1.ResourceRequirements{}
|
||||||
|
limits, err := populateV1ResourceList(params["limits"])
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result.Limits = limits
|
||||||
|
requests, err := populateV1ResourceList(params["requests"])
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result.Requests = requests
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
|
func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
|
||||||
stdin, err := GetBool(params, "stdin", false)
|
stdin, err := GetBool(params, "stdin", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -373,6 +524,36 @@ func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
|
|||||||
return &spec, nil
|
return &spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeV1PodSpec(params map[string]string, name string) (*v1.PodSpec, error) {
|
||||||
|
stdin, err := GetBool(params, "stdin", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tty, err := GetBool(params, "tty", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceRequirements, err := handleV1ResourceRequirements(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
spec := v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: name,
|
||||||
|
Image: params["image"],
|
||||||
|
Stdin: stdin,
|
||||||
|
TTY: tty,
|
||||||
|
Resources: resourceRequirements,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &spec, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (BasicReplicationController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
func (BasicReplicationController) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||||
args, err := getArgs(genericParams)
|
args, err := getArgs(genericParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -455,6 +636,25 @@ func updatePodContainers(params map[string]string, args []string, envs []api.Env
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, podSpec *v1.PodSpec) error {
|
||||||
|
if len(args) > 0 {
|
||||||
|
command, err := GetBool(params, "command", false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if command {
|
||||||
|
podSpec.Containers[0].Command = args
|
||||||
|
} else {
|
||||||
|
podSpec.Containers[0].Args = args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(envs) > 0 {
|
||||||
|
podSpec.Containers[0].Env = envs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) {
|
func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error) {
|
||||||
port := -1
|
port := -1
|
||||||
hostPort := -1
|
hostPort := -1
|
||||||
@ -489,6 +689,40 @@ func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateV1PodPorts(params map[string]string, podSpec *v1.PodSpec) (err error) {
|
||||||
|
port := -1
|
||||||
|
hostPort := -1
|
||||||
|
if len(params["port"]) > 0 {
|
||||||
|
port, err = strconv.Atoi(params["port"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params["hostport"]) > 0 {
|
||||||
|
hostPort, err = strconv.Atoi(params["hostport"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if hostPort > 0 && port < 0 {
|
||||||
|
return fmt.Errorf("--hostport requires --port to be specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't include the port if it was not specified.
|
||||||
|
if port > 0 {
|
||||||
|
podSpec.Containers[0].Ports = []v1.ContainerPort{
|
||||||
|
{
|
||||||
|
ContainerPort: int32(port),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if hostPort > 0 {
|
||||||
|
podSpec.Containers[0].Ports[0].HostPort = int32(hostPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type BasicPod struct{}
|
type BasicPod struct{}
|
||||||
|
|
||||||
func (BasicPod) ParamNames() []GeneratorParam {
|
func (BasicPod) ParamNames() []GeneratorParam {
|
||||||
@ -609,6 +843,24 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) {
|
|||||||
return envs, nil
|
return envs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseV1Envs(envArray []string) ([]v1.EnvVar, error) {
|
||||||
|
envs := []v1.EnvVar{}
|
||||||
|
for _, env := range envArray {
|
||||||
|
pos := strings.Index(env, "=")
|
||||||
|
if pos == -1 {
|
||||||
|
return nil, fmt.Errorf("invalid env: %v", env)
|
||||||
|
}
|
||||||
|
name := env[:pos]
|
||||||
|
value := env[pos+1:]
|
||||||
|
if len(name) == 0 || !validation.IsCIdentifier(name) || len(value) == 0 {
|
||||||
|
return nil, fmt.Errorf("invalid env: %v", env)
|
||||||
|
}
|
||||||
|
envVar := v1.EnvVar{Name: name, Value: value}
|
||||||
|
envs = append(envs, envVar)
|
||||||
|
}
|
||||||
|
return envs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func newBool(val bool) *bool {
|
func newBool(val bool) *bool {
|
||||||
p := new(bool)
|
p := new(bool)
|
||||||
*p = val
|
*p = val
|
||||||
|
Loading…
Reference in New Issue
Block a user