mirror of https://github.com/k3s-io/k3s
Added job/v1 generator to kubectl run
parent
aca37830b1
commit
de83631768
|
@ -40,7 +40,7 @@ Creates a deployment or job to manage the created container(s).
|
|||
|
||||
.PP
|
||||
\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
|
||||
\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.
|
||||
--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
|
||||
--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.
|
||||
--image="": The image for the container to run.
|
||||
-l, --labels="": Labels to apply to the pod(s).
|
||||
|
|
|
@ -485,11 +485,11 @@ runTests() {
|
|||
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:'
|
||||
# Post-condition: valid-pod has the record annotation
|
||||
kube::test::get_object_assert pods "{{range.items}}{{$annotations_field}}:{{end}}" "${change_cause_annotation}"
|
||||
# prove that patch can use different types
|
||||
# prove that patch can use different types
|
||||
kubectl patch "${kube_flags[@]}" pod valid-pod --type="json" -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"nginx2"}]'
|
||||
# Post-condition: valid-pod POD has image nginx
|
||||
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx2:'
|
||||
# prove that patch can use different types
|
||||
# prove that patch can use different types
|
||||
kubectl patch "${kube_flags[@]}" pod valid-pod --type="json" -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"nginx"}]'
|
||||
# Post-condition: valid-pod POD has image nginx
|
||||
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:'
|
||||
|
@ -545,7 +545,7 @@ runTests() {
|
|||
kube::test::get_object_assert 'pod valid-pod' "{{(index .spec.containers 0).name}}" 'replaced-k8s-serve-hostname'
|
||||
#cleaning
|
||||
rm /tmp/tmp-valid-pod.json
|
||||
|
||||
|
||||
## replace of a cluster scoped resource can succeed
|
||||
# Pre-condition: a node exists
|
||||
kubectl create -f - "${kube_flags[@]}" << __EOF__
|
||||
|
@ -755,6 +755,12 @@ __EOF__
|
|||
kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" 'pi:'
|
||||
# Clean up
|
||||
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.
|
||||
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
|
||||
# Pre-Condition: no Deployment exists
|
||||
|
@ -853,7 +859,7 @@ __EOF__
|
|||
# Command
|
||||
[[ "$(kubectl create secret generic test-secret --namespace=test-secrets --from-literal=key1=value1 --output=go-template --template=\"{{.metadata.name}}:\" | grep 'test-secret:')" ]]
|
||||
## Clean-up
|
||||
kubectl delete secret test-secret --namespace=test-secrets
|
||||
kubectl delete secret test-secret --namespace=test-secrets
|
||||
# Clean up
|
||||
kubectl delete namespace test-secrets
|
||||
|
||||
|
@ -884,7 +890,7 @@ __EOF__
|
|||
# Clean-up
|
||||
kubectl delete configmap test-configmap --namespace=test-configmaps
|
||||
kubectl delete namespace test-configmaps
|
||||
|
||||
|
||||
####################
|
||||
# Service Accounts #
|
||||
####################
|
||||
|
|
|
@ -88,7 +88,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
|
|||
}
|
||||
|
||||
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.MarkFlagRequired("image")
|
||||
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 {
|
||||
generatorName = "deployment/v1beta1"
|
||||
} else {
|
||||
generatorName = "job/v1beta1"
|
||||
generatorName = "job/v1"
|
||||
}
|
||||
}
|
||||
generators := f.Generators("run")
|
||||
|
|
|
@ -143,6 +143,7 @@ const (
|
|||
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
|
||||
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
||||
JobV1Beta1GeneratorName = "job/v1beta1"
|
||||
JobV1GeneratorName = "job/v1"
|
||||
NamespaceV1GeneratorName = "namespace/v1"
|
||||
SecretV1GeneratorName = "secret/v1"
|
||||
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
||||
|
@ -161,6 +162,7 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
|
|||
RunPodV1GeneratorName: kubectl.BasicPod{},
|
||||
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
|
||||
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
|
||||
JobV1GeneratorName: kubectl.JobV1{},
|
||||
}
|
||||
generators["autoscale"] = map[string]kubectl.Generator{
|
||||
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
|
||||
|
|
|
@ -24,6 +24,8 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"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/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation"
|
||||
|
@ -187,6 +189,24 @@ func getEnvs(genericParams map[string]interface{}) ([]api.EnvVar, error) {
|
|||
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{}
|
||||
|
||||
func (JobV1Beta1) ParamNames() []GeneratorParam {
|
||||
|
@ -256,7 +276,7 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
|
|||
|
||||
restartPolicy := api.RestartPolicy(params["restart"])
|
||||
if len(restartPolicy) == 0 {
|
||||
restartPolicy = api.RestartPolicyAlways
|
||||
restartPolicy = api.RestartPolicyNever
|
||||
}
|
||||
podSpec.RestartPolicy = restartPolicy
|
||||
|
||||
|
@ -282,6 +302,97 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
|
|||
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{}
|
||||
|
||||
func (BasicReplicationController) ParamNames() []GeneratorParam {
|
||||
|
@ -327,6 +438,30 @@ func populateResourceList(spec string) (api.ResourceList, error) {
|
|||
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
|
||||
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
|
||||
result := api.ResourceRequirements{}
|
||||
|
@ -343,6 +478,22 @@ func HandleResourceRequirements(params map[string]string) (api.ResourceRequireme
|
|||
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) {
|
||||
stdin, err := GetBool(params, "stdin", false)
|
||||
if err != nil {
|
||||
|
@ -373,6 +524,36 @@ func makePodSpec(params map[string]string, name string) (*api.PodSpec, error) {
|
|||
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) {
|
||||
args, err := getArgs(genericParams)
|
||||
if err != nil {
|
||||
|
@ -455,6 +636,25 @@ func updatePodContainers(params map[string]string, args []string, envs []api.Env
|
|||
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) {
|
||||
port := -1
|
||||
hostPort := -1
|
||||
|
@ -489,6 +689,40 @@ func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error)
|
|||
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{}
|
||||
|
||||
func (BasicPod) ParamNames() []GeneratorParam {
|
||||
|
@ -609,6 +843,24 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) {
|
|||
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 {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
|
|
Loading…
Reference in New Issue