From 43b8715d82a713bb2bff5dc53d5e213a42d9d115 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Thu, 10 Aug 2017 13:04:39 +0200 Subject: [PATCH] Promote CronJobs to batch/v1beta1 --- cmd/kube-apiserver/app/aggregator.go | 1 + cmd/kube-apiserver/app/server.go | 1 + hack/.golint_failures | 3 + hack/lib/init.sh | 1 + hack/make-rules/test-cmd-util.sh | 14 +- pkg/api/defaulting_test.go | 15 +- pkg/apis/batch/install/install.go | 4 +- pkg/apis/batch/v1beta1/conversion.go | 44 +++++ pkg/apis/batch/v1beta1/defaults.go | 35 ++++ pkg/apis/batch/v1beta1/defaults_test.go | 104 ++++++++++++ pkg/apis/batch/v1beta1/doc.go | 22 +++ pkg/apis/batch/v1beta1/register.go | 45 ++++++ pkg/master/master.go | 3 + pkg/registry/batch/rest/storage_batch.go | 17 ++ staging/src/k8s.io/api/batch/v1beta1/doc.go | 20 +++ .../src/k8s.io/api/batch/v1beta1/register.go | 53 ++++++ staging/src/k8s.io/api/batch/v1beta1/types.go | 153 ++++++++++++++++++ .../cmd/go-to-protobuf/protobuf/cmd.go | 1 + .../etcd/etcd_storage_path_test.go | 17 +- 19 files changed, 537 insertions(+), 16 deletions(-) create mode 100644 pkg/apis/batch/v1beta1/conversion.go create mode 100644 pkg/apis/batch/v1beta1/defaults.go create mode 100644 pkg/apis/batch/v1beta1/defaults_test.go create mode 100644 pkg/apis/batch/v1beta1/doc.go create mode 100644 pkg/apis/batch/v1beta1/register.go create mode 100644 staging/src/k8s.io/api/batch/v1beta1/doc.go create mode 100644 staging/src/k8s.io/api/batch/v1beta1/register.go create mode 100644 staging/src/k8s.io/api/batch/v1beta1/types.go diff --git a/cmd/kube-apiserver/app/aggregator.go b/cmd/kube-apiserver/app/aggregator.go index 6261a39fc4..2be47d681d 100644 --- a/cmd/kube-apiserver/app/aggregator.go +++ b/cmd/kube-apiserver/app/aggregator.go @@ -182,6 +182,7 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{ {Group: "autoscaling", Version: "v1"}: {group: 17500, version: 15}, {Group: "autoscaling", Version: "v2alpha1"}: {group: 17500, version: 9}, {Group: "batch", Version: "v1"}: {group: 17400, version: 15}, + {Group: "batch", Version: "v1beta1"}: {group: 17400, version: 9}, {Group: "batch", Version: "v2alpha1"}: {group: 17400, version: 9}, {Group: "certificates.k8s.io", Version: "v1beta1"}: {group: 17300, version: 9}, {Group: "networking.k8s.io", Version: "v1"}: {group: 17200, version: 15}, diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 097c93c443..7d0e9a1fc2 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -552,6 +552,7 @@ func BuildStorageFactory(s *options.ServerRunOptions) (*serverstorage.DefaultSto s.Etcd.StorageConfig, s.Etcd.DefaultStorageMediaType, api.Codecs, serverstorage.NewDefaultResourceEncodingConfig(api.Registry), storageGroupsToEncodingVersion, // FIXME: this GroupVersionResource override should be configurable + // TODO we need to update this to batch/v1beta1 when it's enabled by default []schema.GroupVersionResource{batch.Resource("cronjobs").WithVersion("v2alpha1")}, master.DefaultAPIResourceConfigSource(), s.APIEnablement.RuntimeConfig) if err != nil { diff --git a/hack/.golint_failures b/hack/.golint_failures index d6e06a7d23..966603eff0 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -515,6 +515,7 @@ staging/src/k8s.io/api/authorization/v1beta1 staging/src/k8s.io/api/autoscaling/v1 staging/src/k8s.io/api/autoscaling/v2alpha1 staging/src/k8s.io/api/batch/v1 +staging/src/k8s.io/api/batch/v1beta1 staging/src/k8s.io/api/batch/v2alpha1 staging/src/k8s.io/api/certificates/v1beta1 staging/src/k8s.io/api/core/v1 @@ -700,6 +701,8 @@ staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1 staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1/fake staging/src/k8s.io/client-go/kubernetes/typed/batch/v1 staging/src/k8s.io/client-go/kubernetes/typed/batch/v1/fake +staging/src/k8s.io/client-go/kubernetes/typed/batch/v1beta1 +staging/src/k8s.io/client-go/kubernetes/typed/batch/v1beta1/fake staging/src/k8s.io/client-go/kubernetes/typed/batch/v2alpha1 staging/src/k8s.io/client-go/kubernetes/typed/batch/v2alpha1/fake staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1 diff --git a/hack/lib/init.sh b/hack/lib/init.sh index a0c71b5d3d..7ac2db9551 100755 --- a/hack/lib/init.sh +++ b/hack/lib/init.sh @@ -63,6 +63,7 @@ authorization.k8s.io/v1beta1 \ autoscaling/v1 \ autoscaling/v2alpha1 \ batch/v1 \ +batch/v1beta1 \ batch/v2alpha1 \ certificates.k8s.io/v1beta1 \ extensions/v1beta1 \ diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index a490624e16..763e8f116e 100644 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -1184,7 +1184,7 @@ run_kubectl_run_tests() { create_and_use_new_namespace kube::log::status "Testing kubectl run" - ## kubectl run should create deployments or jobs + ## kubectl run should create deployments, jobs or cronjob # Pre-Condition: no Job exists kube::test::get_object_assert jobs "{{range.items}}{{$id_field}}:{{end}}" '' # Command @@ -1197,6 +1197,7 @@ run_kubectl_run_tests() { 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 kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" '' # Command @@ -1218,6 +1219,17 @@ run_kubectl_run_tests() { # Clean up kubectl delete deployment nginx-apps "${kube_flags[@]}" + # TODO: enable batch/v1beta1 by default before 1.8 release, after issues + # with CronJobs existing in multiple versions at once is solved + # # Pre-Condition: no Job exists + # kube::test::get_object_assert cronjobs "{{range.items}}{{$id_field}}:{{end}}" '' + # # Command + # kubectl run pi --schedule="*/5 * * * *" --generator=cronjob/v1beta1 "--image=$IMAGE_PERL" --restart=OnFailure -- perl -Mbignum=bpi -wle 'print bpi(20)' "${kube_flags[@]}" + # # Post-Condition: CronJob "pi" is created + # kube::test::get_object_assert cronjobs "{{range.items}}{{$id_field}}:{{end}}" 'pi:' + # # Clean up + # kubectl delete cronjobs pi "${kube_flags[@]}" + set +o nounset set +o errexit } diff --git a/pkg/api/defaulting_test.go b/pkg/api/defaulting_test.go index b440d68a35..75d9080b6e 100644 --- a/pkg/api/defaulting_test.go +++ b/pkg/api/defaulting_test.go @@ -24,7 +24,6 @@ import ( "github.com/google/gofuzz" - batchv2alpha1 "k8s.io/api/batch/v2alpha1" apiv1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" roundtrip "k8s.io/apimachinery/pkg/api/testing/roundtrip" @@ -33,7 +32,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/diff" "k8s.io/kubernetes/pkg/api" - k8s_batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1" ) type orderedGroupVersionKinds []schema.GroupVersionKind @@ -44,14 +42,6 @@ func (o orderedGroupVersionKinds) Less(i, j int) bool { return o[i].String() < o[j].String() } -func TestVerifyDefaulting(t *testing.T) { - job := &batchv2alpha1.JobTemplate{} - k8s_batchv2alpha1.SetObjectDefaults_JobTemplate(job) - if job.Template.Spec.Template.Spec.DNSPolicy != apiv1.DNSClusterFirst { - t.Errorf("unexpected defaulting: %#v", job) - } -} - // TODO: add a reflexive test that verifies that all SetDefaults functions are registered func TestDefaulting(t *testing.T) { // these are the known types with defaulters - you must add to this list if you add a top level defaulter @@ -90,10 +80,11 @@ func TestDefaulting(t *testing.T) { {Group: "autoscaling", Version: "v2alpha1", Kind: "HorizontalPodAutoscalerList"}: {}, {Group: "batch", Version: "v1", Kind: "Job"}: {}, {Group: "batch", Version: "v1", Kind: "JobList"}: {}, + {Group: "batch", Version: "v1beta1", Kind: "CronJob"}: {}, + {Group: "batch", Version: "v1beta1", Kind: "CronJobList"}: {}, + {Group: "batch", Version: "v1beta1", Kind: "JobTemplate"}: {}, {Group: "batch", Version: "v2alpha1", Kind: "CronJob"}: {}, {Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}: {}, - {Group: "batch", Version: "v2alpha1", Kind: "Job"}: {}, - {Group: "batch", Version: "v2alpha1", Kind: "JobList"}: {}, {Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {}, {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}: {}, {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}: {}, diff --git a/pkg/apis/batch/install/install.go b/pkg/apis/batch/install/install.go index 430bc8eca4..7df8db201e 100644 --- a/pkg/apis/batch/install/install.go +++ b/pkg/apis/batch/install/install.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch/v1" + "k8s.io/kubernetes/pkg/apis/batch/v1beta1" "k8s.io/kubernetes/pkg/apis/batch/v2alpha1" ) @@ -37,11 +38,12 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r if err := announced.NewGroupMetaFactory( &announced.GroupMetaFactoryArgs{ GroupName: batch.GroupName, - VersionPreferenceOrder: []string{v1.SchemeGroupVersion.Version, v2alpha1.SchemeGroupVersion.Version}, + VersionPreferenceOrder: []string{v1.SchemeGroupVersion.Version, v1beta1.SchemeGroupVersion.Version, v2alpha1.SchemeGroupVersion.Version}, AddInternalObjectsToScheme: batch.AddToScheme, }, announced.VersionToSchemeFunc{ v1.SchemeGroupVersion.Version: v1.AddToScheme, + v1beta1.SchemeGroupVersion.Version: v1beta1.AddToScheme, v2alpha1.SchemeGroupVersion.Version: v2alpha1.AddToScheme, }, ).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil { diff --git a/pkg/apis/batch/v1beta1/conversion.go b/pkg/apis/batch/v1beta1/conversion.go new file mode 100644 index 0000000000..bc56896cdd --- /dev/null +++ b/pkg/apis/batch/v1beta1/conversion.go @@ -0,0 +1,44 @@ +/* +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 v1beta1 + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/runtime" +) + +func addConversionFuncs(scheme *runtime.Scheme) error { + var err error + // Add field label conversions for kinds having selectable nothing but ObjectMeta fields. + for _, k := range []string{"Job", "JobTemplate", "CronJob"} { + kind := k // don't close over range variables + err = scheme.AddFieldLabelConversionFunc("batch/v1beta1", kind, + func(label, value string) (string, string, error) { + switch label { + case "metadata.name", "metadata.namespace", "status.successful": + return label, value, nil + default: + return "", "", fmt.Errorf("field label %q not supported for %q", label, kind) + } + }) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/apis/batch/v1beta1/defaults.go b/pkg/apis/batch/v1beta1/defaults.go new file mode 100644 index 0000000000..9ae6847248 --- /dev/null +++ b/pkg/apis/batch/v1beta1/defaults.go @@ -0,0 +1,35 @@ +/* +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 v1beta1 + +import ( + batchv1beta1 "k8s.io/api/batch/v1beta1" + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_CronJob(obj *batchv1beta1.CronJob) { + if obj.Spec.ConcurrencyPolicy == "" { + obj.Spec.ConcurrencyPolicy = batchv1beta1.AllowConcurrent + } + if obj.Spec.Suspend == nil { + obj.Spec.Suspend = new(bool) + } +} diff --git a/pkg/apis/batch/v1beta1/defaults_test.go b/pkg/apis/batch/v1beta1/defaults_test.go new file mode 100644 index 0000000000..57f86e1c46 --- /dev/null +++ b/pkg/apis/batch/v1beta1/defaults_test.go @@ -0,0 +1,104 @@ +/* +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 v1beta1_test + +import ( + "reflect" + "testing" + + batchv1beta1 "k8s.io/api/batch/v1beta1" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/api" + _ "k8s.io/kubernetes/pkg/api/install" + _ "k8s.io/kubernetes/pkg/apis/batch/install" + . "k8s.io/kubernetes/pkg/apis/batch/v1beta1" +) + +func TestSetDefaultCronJob(t *testing.T) { + tests := map[string]struct { + original *batchv1beta1.CronJob + expected *batchv1beta1.CronJob + }{ + "empty batchv2alpha1.CronJob should default batchv2alpha1.ConcurrencyPolicy and Suspend": { + original: &batchv1beta1.CronJob{}, + expected: &batchv1beta1.CronJob{ + Spec: batchv1beta1.CronJobSpec{ + ConcurrencyPolicy: batchv1beta1.AllowConcurrent, + Suspend: newBool(false), + }, + }, + }, + "set fields should not be defaulted": { + original: &batchv1beta1.CronJob{ + Spec: batchv1beta1.CronJobSpec{ + ConcurrencyPolicy: batchv1beta1.ForbidConcurrent, + Suspend: newBool(true), + }, + }, + expected: &batchv1beta1.CronJob{ + Spec: batchv1beta1.CronJobSpec{ + ConcurrencyPolicy: batchv1beta1.ForbidConcurrent, + Suspend: newBool(true), + }, + }, + }, + } + + for name, test := range tests { + original := test.original + expected := test.expected + obj2 := roundTrip(t, runtime.Object(original)) + actual, ok := obj2.(*batchv1beta1.CronJob) + if !ok { + t.Errorf("%s: unexpected object: %v", name, actual) + t.FailNow() + } + if actual.Spec.ConcurrencyPolicy != expected.Spec.ConcurrencyPolicy { + t.Errorf("%s: got different concurrencyPolicy than expected: %v %v", name, actual.Spec.ConcurrencyPolicy, expected.Spec.ConcurrencyPolicy) + } + if *actual.Spec.Suspend != *expected.Spec.Suspend { + t.Errorf("%s: got different suspend than expected: %v %v", name, *actual.Spec.Suspend, *expected.Spec.Suspend) + } + } +} + +func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { + data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) + if err != nil { + t.Errorf("%v\n %#v", err, obj) + return nil + } + obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) + if err != nil { + t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) + return nil + } + obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) + err = api.Scheme.Convert(obj2, obj3, nil) + if err != nil { + t.Errorf("%v\nSource: %#v", err, obj2) + return nil + } + return obj3 +} + +func newBool(val bool) *bool { + p := new(bool) + *p = val + return p +} diff --git a/pkg/apis/batch/v1beta1/doc.go b/pkg/apis/batch/v1beta1/doc.go new file mode 100644 index 0000000000..6c3e65dcb3 --- /dev/null +++ b/pkg/apis/batch/v1beta1/doc.go @@ -0,0 +1,22 @@ +/* +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. +*/ + +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/batch +// +k8s:conversion-gen-external-types=../../../../vendor/k8s.io/api/batch/v1beta1 +// +k8s:defaulter-gen=TypeMeta +// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/batch/v1beta1 + +package v1beta1 // import "k8s.io/kubernetes/pkg/apis/batch/v1beta1" diff --git a/pkg/apis/batch/v1beta1/register.go b/pkg/apis/batch/v1beta1/register.go new file mode 100644 index 0000000000..d90cdfbb15 --- /dev/null +++ b/pkg/apis/batch/v1beta1/register.go @@ -0,0 +1,45 @@ +/* +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 v1beta1 + +import ( + batchv1beta1 "k8s.io/api/batch/v1beta1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "batch" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &batchv1beta1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs, addConversionFuncs) +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 107c4aabd0..8d1fca18ce 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -386,6 +386,9 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, + // TODO: enable batch/v1beta1 by default before 1.8 release, after issues + // with CronJobs existing in multiple versions at once is solved + // batchapiv1beta1.SchemeGroupVersion, authenticationv1.SchemeGroupVersion, authenticationv1beta1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, diff --git a/pkg/registry/batch/rest/storage_batch.go b/pkg/registry/batch/rest/storage_batch.go index c11d1ddc15..6cfd8f4a64 100644 --- a/pkg/registry/batch/rest/storage_batch.go +++ b/pkg/registry/batch/rest/storage_batch.go @@ -18,6 +18,7 @@ package rest import ( batchapiv1 "k8s.io/api/batch/v1" + batchapiv1beta1 "k8s.io/api/batch/v1beta1" batchapiv2alpha1 "k8s.io/api/batch/v2alpha1" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" @@ -40,6 +41,10 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag apiGroupInfo.VersionedResourcesStorageMap[batchapiv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter) apiGroupInfo.GroupMeta.GroupVersion = batchapiv1.SchemeGroupVersion } + if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv1beta1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[batchapiv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter) + apiGroupInfo.GroupMeta.GroupVersion = batchapiv1beta1.SchemeGroupVersion + } if apiResourceConfigSource.AnyResourcesForVersionEnabled(batchapiv2alpha1.SchemeGroupVersion) { apiGroupInfo.VersionedResourcesStorageMap[batchapiv2alpha1.SchemeGroupVersion.Version] = p.v2alpha1Storage(apiResourceConfigSource, restOptionsGetter) apiGroupInfo.GroupMeta.GroupVersion = batchapiv2alpha1.SchemeGroupVersion @@ -60,6 +65,18 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API return storage } +func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage { + version := batchapiv1beta1.SchemeGroupVersion + + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("cronjobs")) { + cronJobsStorage, cronJobsStatusStorage := cronjobstore.NewREST(restOptionsGetter) + storage["cronjobs"] = cronJobsStorage + storage["cronjobs/status"] = cronJobsStatusStorage + } + return storage +} + func (p RESTStorageProvider) v2alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage { version := batchapiv2alpha1.SchemeGroupVersion diff --git a/staging/src/k8s.io/api/batch/v1beta1/doc.go b/staging/src/k8s.io/api/batch/v1beta1/doc.go new file mode 100644 index 0000000000..653ebe0403 --- /dev/null +++ b/staging/src/k8s.io/api/batch/v1beta1/doc.go @@ -0,0 +1,20 @@ +/* +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. +*/ + +// +k8s:deepcopy-gen=package,register +// +k8s:openapi-gen=true + +package v1beta1 // import "k8s.io/api/batch/v1beta1" diff --git a/staging/src/k8s.io/api/batch/v1beta1/register.go b/staging/src/k8s.io/api/batch/v1beta1/register.go new file mode 100644 index 0000000000..569817d3e4 --- /dev/null +++ b/staging/src/k8s.io/api/batch/v1beta1/register.go @@ -0,0 +1,53 @@ +/* +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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "batch" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &JobTemplate{}, + &CronJob{}, + &CronJobList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/staging/src/k8s.io/api/batch/v1beta1/types.go b/staging/src/k8s.io/api/batch/v1beta1/types.go new file mode 100644 index 0000000000..43e447473d --- /dev/null +++ b/staging/src/k8s.io/api/batch/v1beta1/types.go @@ -0,0 +1,153 @@ +/* +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 v1beta1 + +import ( + batchv1 "k8s.io/api/batch/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// JobTemplate describes a template for creating copies of a predefined pod. +type JobTemplate struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Defines jobs that will be created from this template. + // https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Template JobTemplateSpec `json:"template,omitempty" protobuf:"bytes,2,opt,name=template"` +} + +// JobTemplateSpec describes the data a Job should have when created from a template +type JobTemplateSpec struct { + // Standard object's metadata of the jobs created from this template. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Specification of the desired behavior of the job. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Spec batchv1.JobSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CronJob represents the configuration of a single cron job. +type CronJob struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Specification of the desired behavior of a cron job, including the schedule. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Spec CronJobSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + + // Current status of a cron job. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Status CronJobStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CronJobList is a collection of cron jobs. +type CronJobList struct { + metav1.TypeMeta `json:",inline"` + + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // items is the list of CronJobs. + Items []CronJob `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// CronJobSpec describes how the job execution will look like and when it will actually run. +type CronJobSpec struct { + + // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + Schedule string `json:"schedule" protobuf:"bytes,1,opt,name=schedule"` + + // Optional deadline in seconds for starting the job if it misses scheduled + // time for any reason. Missed jobs executions will be counted as failed ones. + // +optional + StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty" protobuf:"varint,2,opt,name=startingDeadlineSeconds"` + + // Specifies how to treat concurrent executions of a Job. + // Defaults to Allow. + // +optional + ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty" protobuf:"bytes,3,opt,name=concurrencyPolicy,casttype=ConcurrencyPolicy"` + + // This flag tells the controller to suspend subsequent executions, it does + // not apply to already started executions. Defaults to false. + // +optional + Suspend *bool `json:"suspend,omitempty" protobuf:"varint,4,opt,name=suspend"` + + // Specifies the job that will be created when executing a CronJob. + JobTemplate JobTemplateSpec `json:"jobTemplate" protobuf:"bytes,5,opt,name=jobTemplate"` + + // The number of successful finished jobs to retain. + // This is a pointer to distinguish between explicit zero and not specified. + // +optional + SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty" protobuf:"varint,6,opt,name=successfulJobsHistoryLimit"` + + // The number of failed finished jobs to retain. + // This is a pointer to distinguish between explicit zero and not specified. + // +optional + FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty" protobuf:"varint,7,opt,name=failedJobsHistoryLimit"` +} + +// ConcurrencyPolicy describes how the job will be handled. +// Only one of the following concurrent policies may be specified. +// If none of the following policies is specified, the default one +// is AllowConcurrent. +type ConcurrencyPolicy string + +const ( + // AllowConcurrent allows CronJobs to run concurrently. + AllowConcurrent ConcurrencyPolicy = "Allow" + + // ForbidConcurrent forbids concurrent runs, skipping next run if previous + // hasn't finished yet. + ForbidConcurrent ConcurrencyPolicy = "Forbid" + + // ReplaceConcurrent cancels currently running job and replaces it with a new one. + ReplaceConcurrent ConcurrencyPolicy = "Replace" +) + +// CronJobStatus represents the current state of a cron job. +type CronJobStatus struct { + // A list of pointers to currently running jobs. + // +optional + Active []v1.ObjectReference `json:"active,omitempty" protobuf:"bytes,1,rep,name=active"` + + // Information when was the last time the job was successfully scheduled. + // +optional + LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty" protobuf:"bytes,4,opt,name=lastScheduleTime"` +} diff --git a/staging/src/k8s.io/kube-gen/cmd/go-to-protobuf/protobuf/cmd.go b/staging/src/k8s.io/kube-gen/cmd/go-to-protobuf/protobuf/cmd.go index 794ac5a183..d8c35f9c6c 100644 --- a/staging/src/k8s.io/kube-gen/cmd/go-to-protobuf/protobuf/cmd.go +++ b/staging/src/k8s.io/kube-gen/cmd/go-to-protobuf/protobuf/cmd.go @@ -79,6 +79,7 @@ func New() *Generator { `k8s.io/api/autoscaling/v2alpha1`, `k8s.io/api/authorization/v1beta1`, `k8s.io/api/batch/v1`, + `k8s.io/api/batch/v1beta1`, `k8s.io/api/batch/v2alpha1`, `k8s.io/api/apps/v1beta1`, `k8s.io/api/apps/v1beta2`, diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index 62a63a832d..8eaae85fea 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -192,10 +192,19 @@ var etcdStorageData = map[schema.GroupVersionResource]struct { }, // -- + // k8s.io/kubernetes/pkg/apis/batch/v1beta1 + gvr("batch", "v1beta1", "cronjobs"): { + stub: `{"metadata": {"name": "cjv1beta1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, + expectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cjv1beta1", + // TODO this needs to be updated to batch/v1beta1 when it's enabed by default + expectedGVK: gvkP("batch", "v2alpha1", "CronJob"), + }, + // -- + // k8s.io/kubernetes/pkg/apis/batch/v2alpha1 gvr("batch", "v2alpha1", "cronjobs"): { - stub: `{"metadata": {"name": "cj1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, - expectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cj1", + stub: `{"metadata": {"name": "cjv2alpha1"}, "spec": {"jobTemplate": {"spec": {"template": {"metadata": {"labels": {"controller-uid": "uid0"}}, "spec": {"containers": [{"image": "fedora:latest", "name": "container0"}], "dnsPolicy": "ClusterFirst", "restartPolicy": "Never"}}}}, "schedule": "* * * * *"}}`, + expectedEtcdPath: "/registry/cronjobs/etcdstoragepathtestnamespace/cjv2alpha1", }, // -- @@ -407,6 +416,10 @@ var ephemeralWhiteList = createEphemeralWhiteList( gvr("apps", "v1beta2", "scales"), // not stored in etcd, part of kapiv1.ReplicationController // -- + // k8s.io/kubernetes/pkg/apis/batch/v1beta1 + gvr("batch", "v1beta1", "jobtemplates"), // not stored in etcd + // -- + // k8s.io/kubernetes/pkg/apis/batch/v2alpha1 gvr("batch", "v2alpha1", "jobtemplates"), // not stored in etcd // --