Merge pull request #21434 from erictune/job-ga

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot 2016-02-22 00:12:54 -08:00
commit aee2eb3977
56 changed files with 11314 additions and 177 deletions

View File

@ -0,0 +1,30 @@
{
"swaggerVersion": "1.2",
"apiVersion": "",
"basePath": "https://10.10.10.10:443",
"resourcePath": "/apis/batch",
"apis": [
{
"path": "/apis/batch",
"description": "get information of a group",
"operations": [
{
"type": "void",
"method": "GET",
"summary": "get information of a group",
"nickname": "getAPIGroup",
"parameters": [],
"produces": [
"application/json",
"application/yaml"
],
"consumes": [
"application/json",
"application/yaml"
]
}
]
}
],
"models": {}
}

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,14 @@
{
"path": "/apis/autoscaling",
"description": "get information of a group"
},
{
"path": "/apis/batch/v1",
"description": "API at /apis/batch/v1"
},
{
"path": "/apis/batch",
"description": "get information of a group"
}
],
"apiVersion": "",

View File

@ -28,6 +28,7 @@ import (
_ "k8s.io/kubernetes/pkg/api/install"
"k8s.io/kubernetes/pkg/api/unversioned"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install"

View File

@ -29,6 +29,7 @@ import (
_ "k8s.io/kubernetes/pkg/api/install"
"k8s.io/kubernetes/pkg/api/unversioned"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install"

View File

@ -38,6 +38,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apiserver"
"k8s.io/kubernetes/pkg/apiserver/authenticator"
@ -305,6 +306,11 @@ func Run(s *options.APIServer) error {
// sure autoscaling has a storage destination. If the autoscaling group
// itself is on, it will overwrite this decision below.
storageDestinations.AddAPIGroup(autoscaling.GroupName, expEtcdStorage)
// Since Job has been moved to the batch group, we need to make
// sure batch has a storage destination. If the batch group
// itself is on, it will overwrite this decision below.
storageDestinations.AddAPIGroup(batch.GroupName, expEtcdStorage)
}
// autoscaling/v1/horizontalpodautoscalers is a move from extensions/v1beta1/horizontalpodautoscalers.
@ -333,6 +339,33 @@ func Run(s *options.APIServer) error {
storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage)
}
// batch/v1/job is a move from extensions/v1beta1/job. The storage
// version needs to be either extensions/v1beta1 or batch/v1. Users
// must roll forward while using 1.2, because we will require the
// latter for 1.3.
if !apiGroupVersionOverrides["batch/v1"].Disable {
glog.Infof("Configuring batch/v1 storage destination")
batchGroup, err := registered.Group(batch.GroupName)
if err != nil {
glog.Fatalf("Batch API is enabled in runtime config, but not enabled in the environment variable KUBE_API_VERSIONS. Error: %v", err)
}
// Figure out what storage group/version we should use.
storageGroupVersion, found := storageVersions[batchGroup.GroupVersion.Group]
if !found {
glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", batchGroup.GroupVersion.Group, storageVersions)
}
if storageGroupVersion != "batch/v1" && storageGroupVersion != "extensions/v1beta1" {
glog.Fatalf("The storage version for batch must be either 'batch/v1' or 'extensions/v1beta1'")
}
glog.Infof("Using %v for batch group storage version", storageGroupVersion)
batchEtcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageGroupVersion, "extensions/__internal", s.EtcdPathPrefix, s.EtcdQuorumRead)
if err != nil {
glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err)
}
storageDestinations.AddAPIGroup(batch.GroupName, batchEtcdStorage)
}
updateEtcdOverrides(s.EtcdServersOverrides, storageVersions, s.EtcdPathPrefix, s.EtcdQuorumRead, &storageDestinations, newEtcd)
n := s.ServiceClusterIPRange
@ -520,6 +553,14 @@ func parseRuntimeConfig(s *options.APIServer) (map[string]genericapiserver.APIGr
Disable: true,
}
}
disableBatch := disableAllAPIs
batchGroupVersion := "batch/v1"
disableBatch = !getRuntimeConfigValue(s, batchGroupVersion, !disableBatch)
if disableBatch {
apiGroupVersionOverrides[batchGroupVersion] = genericapiserver.APIGroupVersionOverride{
Disable: true,
}
}
for key := range s.RuntimeConfig {
if strings.HasPrefix(key, v1GroupVersion+"/") {

View File

@ -394,6 +394,7 @@ _kubectl_describe()
must_have_one_noun+=("horizontalpodautoscaler")
must_have_one_noun+=("ingress")
must_have_one_noun+=("job")
must_have_one_noun+=("job")
must_have_one_noun+=("limitrange")
must_have_one_noun+=("namespace")
must_have_one_noun+=("node")

View File

@ -102,7 +102,7 @@ kube-apiserver
--service-node-port-range=: A port range to reserve for services with NodePort visibility. Example: '30000-32767'. Inclusive at both ends of the range.
--ssh-keyfile="": If non-empty, use secure SSH proxy to the nodes, using this user keyfile
--ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name
--storage-versions="authorization.k8s.io/v1beta1,autoscaling/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
--storage-versions="authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
--tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.
--tls-private-key-file="": File containing x509 private key matching --tls-cert-file.
--token-auth-file="": If set, the file that will be used to secure the secure port of the API server via token authentication.
@ -110,7 +110,7 @@ kube-apiserver
--watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled.
```
###### Auto generated by spf13/cobra on 15-Feb-2016
###### Auto generated by spf13/cobra on 18-Feb-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->

View File

@ -43,7 +43,7 @@ EOF
}
# TODO(lavalamp): get this list by listing the pkg/apis/ directory?
DEFAULT_GROUP_VERSIONS="v1 authorization/v1beta1 autoscaling/v1 extensions/v1beta1 componentconfig/v1alpha1 metrics/v1alpha1"
DEFAULT_GROUP_VERSIONS="v1 authorization/v1beta1 autoscaling/v1 batch/v1 extensions/v1beta1 componentconfig/v1alpha1 metrics/v1alpha1"
VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS}
for ver in $VERSIONS; do
# Ensure that the version being processed is registered by setting

View File

@ -62,6 +62,6 @@ function generate_deep_copies() {
# Currently pkg/api/deep_copy_generated.go is generated by the new go2idl generator.
# All others (mentioned above) are still generated by the old reflection-based generator.
# TODO: Migrate these to the new generator.
DEFAULT_VERSIONS="v1 authorization/__internal authorization/v1beta1 autoscaling/__internal autoscaling/v1 extensions/__internal extensions/v1beta1 componentconfig/__internal componentconfig/v1alpha1 metrics/__internal metrics/v1alpha1"
DEFAULT_VERSIONS="v1 authorization/__internal authorization/v1beta1 autoscaling/__internal autoscaling/v1 batch/__internal batch/v1 extensions/__internal extensions/v1beta1 componentconfig/__internal componentconfig/v1alpha1 metrics/__internal metrics/v1alpha1"
VERSIONS=${VERSIONS:-$DEFAULT_VERSIONS}
generate_deep_copies "$VERSIONS"

View File

@ -50,7 +50,7 @@ kube::etcd::start
# Start kube-apiserver
kube::log::status "Starting kube-apiserver"
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
--address="127.0.0.1" \
--public-address-override="127.0.0.1" \
--port="${API_PORT}" \
@ -64,7 +64,7 @@ APISERVER_PID=$!
kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: "
SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/"
DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 extensions/v1beta1"
DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 batch/v1 extensions/v1beta1"
VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS}
kube::log::status "Updating " ${SWAGGER_ROOT_DIR}

View File

@ -23,7 +23,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh"
kube::golang::setup_env
APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/autoscaling pkg/apis/extensions pkg/apis/metrics}
APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/autoscaling pkg/apis/batch pkg/apis/extensions pkg/apis/metrics}
_tmp="${KUBE_ROOT}/_tmp"
cleanup() {

View File

@ -25,7 +25,7 @@ kube::golang::setup_env
gendeepcopy=$(kube::util::find-binary "gendeepcopy")
APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/autoscaling pkg/apis/extensions pkg/apis/metrics}
APIROOTS=${APIROOTS:-pkg/api pkg/apis/authorization pkg/apis/autoscaling pkg/apis/batch pkg/apis/extensions pkg/apis/metrics}
_tmp="${KUBE_ROOT}/_tmp"
cleanup() {

View File

@ -178,7 +178,7 @@ kube::log::status "Starting kube-apiserver"
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL="NamespaceLifecycle,LimitRanger,ResourceQuota"
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
--address="127.0.0.1" \
--public-address-override="127.0.0.1" \
--port="${API_PORT}" \
@ -727,6 +727,8 @@ __EOF__
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
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
@ -1604,7 +1606,7 @@ kube_api_versions=(
v1
)
for version in "${kube_api_versions[@]}"; do
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" runTests "${version}"
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" runTests "${version}"
done
kube::log::status "TEST PASSED"

View File

@ -58,7 +58,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-}
# Lists of API Versions of each groups that should be tested, groups are
# separated by comma, lists are separated by semicolon. e.g.,
# "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1;v1,autoscaling/v1,extensions/v1beta1,metrics/v1alpha1"}
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1;v1,autoscaling/v1,batch/v1,extensions/v1beta1,metrics/v1alpha1"}
# once we have multiple group supports
# Run tests with the standard (registry) and a custom etcd prefix
# (kubernetes.io/registry).
@ -315,7 +315,7 @@ for (( i=0, j=0; ; )); do
# KUBE_TEST_API sets the version of each group to be tested. KUBE_API_VERSIONS
# register the groups/versions as supported by k8s. So KUBE_API_VERSIONS
# needs to be the superset of KUBE_TEST_API.
KUBE_TEST_API="${apiVersion}" KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1,componentconfig/v1alpha1,metrics/v1alpha1" ETCD_PREFIX=${etcdPrefix} runTests "$@"
KUBE_TEST_API="${apiVersion}" KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1,componentconfig/v1alpha1,metrics/v1alpha1" ETCD_PREFIX=${etcdPrefix} runTests "$@"
i=${i}+1
j=${j}+1
if [[ i -eq ${apiVersionsCount} ]] && [[ j -eq ${etcdPrefixesCount} ]]; then

View File

@ -29,7 +29,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh"
# "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
# TODO: It's going to be:
# KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"}
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,extensions/v1beta1"}
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,batch/v1,extensions/v1beta1"}
# Give integration tests longer to run
KUBE_TIMEOUT=${KUBE_TIMEOUT:--timeout 240s}
@ -52,21 +52,21 @@ runTests() {
KUBE_RACE="" \
KUBE_TIMEOUT="${KUBE_TIMEOUT}" \
KUBE_TEST_API_VERSIONS="$1" \
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" \
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" \
"${KUBE_ROOT}/hack/test-go.sh" test/integration
kube::log::status "Running integration test scenario with watch cache on"
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \
--max-concurrency="${KUBE_INTEGRATION_TEST_MAX_CONCURRENCY}" --watch-cache=true
kube::log::status "Running integration test scenario with watch cache off"
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" KUBE_TEST_API_VERSIONS="$1" "${KUBE_OUTPUT_HOSTBIN}/integration" --v=${LOG_LEVEL} \
--max-concurrency="${KUBE_INTEGRATION_TEST_MAX_CONCURRENCY}" --watch-cache=false
cleanup
}
KUBE_API_VERSIONS="v1,autoscaling/v1,extensions/v1beta1" "${KUBE_ROOT}/hack/build-go.sh" "$@" cmd/integration
KUBE_API_VERSIONS="v1,autoscaling/v1,batch/v1,extensions/v1beta1" "${KUBE_ROOT}/hack/build-go.sh" "$@" cmd/integration
# Run cleanup to stop etcd on interrupt or other kill signal.
trap cleanup EXIT

View File

@ -33,7 +33,7 @@ function generate_version() {
echo "package ${group_version##*/}" >> "$TMPFILE"
cat >> "$TMPFILE" <<EOF
// This file contains a collection of methods that can be used from go-resful to
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
@ -56,7 +56,7 @@ EOF
mv "$TMPFILE" "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go"
}
GROUP_VERSIONS=(unversioned v1 authorization/v1beta1 autoscaling/v1 extensions/v1beta1)
GROUP_VERSIONS=(unversioned v1 authorization/v1beta1 autoscaling/v1 batch/v1 extensions/v1beta1)
# To avoid compile errors, remove the currently existing files.
for group_version in "${GROUP_VERSIONS[@]}"; do
rm -f "pkg/$(kube::util::group-version-to-pkg-path "${group_version}")/types_swagger_doc_generated.go"

View File

@ -27,11 +27,13 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
_ "k8s.io/kubernetes/pkg/api/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install"
@ -41,6 +43,7 @@ var (
Groups = make(map[string]TestGroup)
Default TestGroup
Autoscaling TestGroup
Batch TestGroup
Extensions TestGroup
)
@ -72,21 +75,28 @@ func init() {
internalGroupVersion: api.SchemeGroupVersion,
}
}
if _, ok := Groups[autoscaling.GroupName]; !ok {
Groups[autoscaling.GroupName] = TestGroup{
externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version},
internalGroupVersion: extensions.SchemeGroupVersion,
}
}
if _, ok := Groups[extensions.GroupName]; !ok {
Groups[extensions.GroupName] = TestGroup{
externalGroupVersion: unversioned.GroupVersion{Group: extensions.GroupName, Version: registered.GroupOrDie(extensions.GroupName).GroupVersion.Version},
internalGroupVersion: extensions.SchemeGroupVersion,
}
}
if _, ok := Groups[autoscaling.GroupName]; !ok {
Groups[autoscaling.GroupName] = TestGroup{
externalGroupVersion: unversioned.GroupVersion{Group: autoscaling.GroupName, Version: registered.GroupOrDie(autoscaling.GroupName).GroupVersion.Version},
internalGroupVersion: extensions.SchemeGroupVersion,
}
}
if _, ok := Groups[batch.GroupName]; !ok {
Groups[batch.GroupName] = TestGroup{
externalGroupVersion: unversioned.GroupVersion{Group: batch.GroupName, Version: registered.GroupOrDie(batch.GroupName).GroupVersion.Version},
internalGroupVersion: extensions.SchemeGroupVersion,
}
}
Default = Groups[api.GroupName]
Autoscaling = Groups[autoscaling.GroupName]
Batch = Groups[batch.GroupName]
Extensions = Groups[extensions.GroupName]
}

View File

@ -128,6 +128,10 @@ func TestAutoscalingEncodeDecodeStatus(t *testing.T) {
testEncodeDecodeStatus(t, Autoscaling.Codec())
}
func TestBatchEncodeDecodeStatus(t *testing.T) {
testEncodeDecodeStatus(t, Batch.Codec())
}
func TestExperimentalEncodeDecodeStatus(t *testing.T) {
testEncodeDecodeStatus(t, Extensions.Codec())
}

View File

@ -16,7 +16,7 @@ limitations under the License.
package unversioned
// This file contains a collection of methods that can be used from go-resful to
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//

View File

@ -16,7 +16,7 @@ limitations under the License.
package v1
// This file contains a collection of methods that can be used from go-resful to
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//

View File

@ -16,7 +16,7 @@ limitations under the License.
package v1beta1
// This file contains a collection of methods that can be used from go-resful to
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//

View File

@ -16,7 +16,7 @@ limitations under the License.
package v1
// This file contains a collection of methods that can be used from go-resful to
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//

View File

@ -0,0 +1,29 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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.
*/
// DO NOT EDIT. THIS FILE IS AUTO-GENERATED BY $KUBEROOT/hack/update-generated-deep-copies.sh.
package batch
import api "k8s.io/kubernetes/pkg/api"
func init() {
err := api.Scheme.AddGeneratedDeepCopyFuncs()
if err != nil {
// if one of the deep copy functions is malformed, detect it immediately.
panic(err)
}
}

View File

@ -0,0 +1,129 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 install installs the batch API group, making it available as
// an option to all of the API encoding/decoding machinery.
package install
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/sets"
)
const importPrefix = "k8s.io/kubernetes/pkg/apis/batch"
var accessor = meta.NewAccessor()
// availableVersions lists all known external versions for this group from most preferred to least preferred
var availableVersions = []unversioned.GroupVersion{v1.SchemeGroupVersion}
func init() {
registered.RegisterVersions(availableVersions)
externalVersions := []unversioned.GroupVersion{}
for _, v := range availableVersions {
if registered.IsAllowedVersion(v) {
externalVersions = append(externalVersions, v)
}
}
if len(externalVersions) == 0 {
glog.V(4).Infof("No version is registered for group %v", batch.GroupName)
return
}
if err := registered.EnableVersions(externalVersions...); err != nil {
glog.V(4).Infof("%v", err)
return
}
if err := enableVersions(externalVersions); err != nil {
glog.V(4).Infof("%v", err)
return
}
}
// TODO: enableVersions should be centralized rather than spread in each API
// group.
// We can combine registered.RegisterVersions, registered.EnableVersions and
// registered.RegisterGroup once we have moved enableVersions there.
func enableVersions(externalVersions []unversioned.GroupVersion) error {
addVersionsToScheme(externalVersions...)
preferredExternalVersion := externalVersions[0]
groupMeta := apimachinery.GroupMeta{
GroupVersion: preferredExternalVersion,
GroupVersions: externalVersions,
RESTMapper: newRESTMapper(externalVersions),
SelfLinker: runtime.SelfLinker(accessor),
InterfacesFor: interfacesFor,
}
if err := registered.RegisterGroup(groupMeta); err != nil {
return err
}
api.RegisterRESTMapper(groupMeta.RESTMapper)
return nil
}
func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper {
// the list of kinds that are scoped at the root of the api hierarchy
// if a kind is not enumerated here, it is assumed to have a namespace scope
rootScoped := sets.NewString()
ignoredKinds := sets.NewString()
return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
}
// interfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
switch version {
case v1.SchemeGroupVersion:
return &meta.VersionInterfaces{
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
default:
g, _ := registered.Group(batch.GroupName)
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions)
}
}
func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) {
// add the internal version to Scheme
batch.AddToScheme(api.Scheme)
// add the enabled external versions to Scheme
for _, v := range externalVersions {
if !registered.IsEnabledVersion(v) {
glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
continue
}
switch v {
case v1.SchemeGroupVersion:
v1.AddToScheme(api.Scheme)
}
}
}

View File

@ -0,0 +1,54 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 batch
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
)
// GroupName is the group name use in this package
const GroupName = "batch"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) unversioned.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns back a Group qualified GroupResource
func Resource(resource string) unversioned.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func AddToScheme(scheme *runtime.Scheme) {
// Add the API to Scheme.
addKnownTypes(scheme)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) {
scheme.AddKnownTypes(SchemeGroupVersion,
&extensions.Job{},
&extensions.JobList{},
&api.ListOptions{},
)
}

View File

@ -0,0 +1,63 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 v1
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
)
func addConversionFuncs(scheme *runtime.Scheme) {
// Add non-generated conversion functions
err := scheme.AddConversionFuncs(
Convert_api_PodSpec_To_v1_PodSpec,
Convert_v1_PodSpec_To_api_PodSpec,
)
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = api.Scheme.AddFieldLabelConversionFunc("batch/v1", "Job",
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 not supported: %s", label)
}
})
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
}
// The following two PodSpec conversions functions where copied from pkg/api/conversion.go
// for the generated functions to work properly.
// This should be fixed: https://github.com/kubernetes/kubernetes/issues/12977
func Convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *v1.PodSpec, s conversion.Scope) error {
return v1.Convert_api_PodSpec_To_v1_PodSpec(in, out, s)
}
func Convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conversion.Scope) error {
return v1.Convert_v1_PodSpec_To_api_PodSpec(in, out, s)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 v1
import (
"k8s.io/kubernetes/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) {
scheme.AddDefaultingFuncs(
func(obj *Job) {
labels := obj.Spec.Template.Labels
// TODO: support templates defined elsewhere when we support them in the API
if labels != nil {
if obj.Spec.Selector == nil {
obj.Spec.Selector = &LabelSelector{
MatchLabels: labels,
}
}
if len(obj.Labels) == 0 {
obj.Labels = labels
}
}
// For a non-parallel job, you can leave both `.spec.completions` and
// `.spec.parallelism` unset. When both are unset, both are defaulted to 1.
if obj.Spec.Completions == nil && obj.Spec.Parallelism == nil {
obj.Spec.Completions = new(int32)
*obj.Spec.Completions = 1
obj.Spec.Parallelism = new(int32)
*obj.Spec.Parallelism = 1
}
if obj.Spec.Parallelism == nil {
obj.Spec.Parallelism = new(int32)
*obj.Spec.Parallelism = 1
}
},
)
}

View File

@ -0,0 +1,47 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 v1
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
)
// GroupName is the group name use in this package
const GroupName = "batch"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"}
func AddToScheme(scheme *runtime.Scheme) {
addKnownTypes(scheme)
addDefaultingFuncs(scheme)
addConversionFuncs(scheme)
}
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) {
scheme.AddKnownTypes(SchemeGroupVersion,
&Job{},
&JobList{},
&v1.ListOptions{},
)
}
func (obj *Job) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *JobList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

File diff suppressed because it is too large Load Diff

170
pkg/apis/batch/v1/types.go Normal file
View File

@ -0,0 +1,170 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 v1
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
)
// Job represents the configuration of a single job.
type Job struct {
unversioned.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
v1.ObjectMeta `json:"metadata,omitempty"`
// Spec is a structure defining the expected behavior of a job.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Spec JobSpec `json:"spec,omitempty"`
// Status is a structure describing current status of a job.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Status JobStatus `json:"status,omitempty"`
}
// JobList is a collection of jobs.
type JobList struct {
unversioned.TypeMeta `json:",inline"`
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
unversioned.ListMeta `json:"metadata,omitempty"`
// Items is the list of Job.
Items []Job `json:"items"`
}
// JobSpec describes how the job execution will look like.
type JobSpec struct {
// Parallelism specifies the maximum desired number of pods the job should
// run at any given time. The actual number of pods running in steady state will
// be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism),
// i.e. when the work left to do is less than max parallelism.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
Parallelism *int32 `json:"parallelism,omitempty"`
// Completions specifies the desired number of successfully finished pods the
// job should be run with. Setting to nil means that the success of any
// pod signals the success of all pods, and allows parallelism to have any positive
// value. Setting to 1 means that parallelism is limited to 1 and the success of that
// pod signals the success of the job.
Completions *int32 `json:"completions,omitempty"`
// Optional duration in seconds relative to the startTime that the job may be active
// before the system tries to terminate it; value must be positive integer
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
// Selector is a label query over pods that should match the pod count.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors
Selector *LabelSelector `json:"selector,omitempty"`
// Template is the object that describes the pod that will be created when
// executing a job.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
Template v1.PodTemplateSpec `json:"template"`
}
// JobStatus represents the current state of a Job.
type JobStatus struct {
// Conditions represent the latest available observations of an object's current state.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md
Conditions []JobCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// StartTime represents time when the job was acknowledged by the Job Manager.
// It is not guaranteed to be set in happens-before order across separate operations.
// It is represented in RFC3339 form and is in UTC.
StartTime *unversioned.Time `json:"startTime,omitempty"`
// CompletionTime represents time when the job was completed. It is not guaranteed to
// be set in happens-before order across separate operations.
// It is represented in RFC3339 form and is in UTC.
CompletionTime *unversioned.Time `json:"completionTime,omitempty"`
// Active is the number of actively running pods.
Active int32 `json:"active,omitempty"`
// Succeeded is the number of pods which reached Phase Succeeded.
Succeeded int32 `json:"succeeded,omitempty"`
// Failed is the number of pods which reached Phase Failed.
Failed int32 `json:"failed,omitempty"`
}
type JobConditionType string
// These are valid conditions of a job.
const (
// JobComplete means the job has completed its execution.
JobComplete JobConditionType = "Complete"
// JobFailed means the job has failed its execution.
JobFailed JobConditionType = "Failed"
)
// JobCondition describes current state of a job.
type JobCondition struct {
// Type of job condition, Complete or Failed.
Type JobConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status v1.ConditionStatus `json:"status"`
// Last time the condition was checked.
LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"`
// Last time the condition transit from one status to another.
LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty"`
// (brief) reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// Human readable message indicating details about last transition.
Message string `json:"message,omitempty"`
}
// A label selector is a label query over a set of resources. The result of matchLabels and
// matchExpressions are ANDed. An empty label selector matches all objects. A null
// label selector matches no objects.
type LabelSelector struct {
// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
// map is equivalent to an element of matchExpressions, whose key field is "key", the
// operator is "In", and the values array contains only "value". The requirements are ANDed.
MatchLabels map[string]string `json:"matchLabels,omitempty"`
// matchExpressions is a list of label selector requirements. The requirements are ANDed.
MatchExpressions []LabelSelectorRequirement `json:"matchExpressions,omitempty"`
}
// A label selector requirement is a selector that contains values, a key, and an operator that
// relates the key and values.
type LabelSelectorRequirement struct {
// key is the label key that the selector applies to.
Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key"`
// operator represents a key's relationship to a set of values.
// Valid operators ard In, NotIn, Exists and DoesNotExist.
Operator LabelSelectorOperator `json:"operator"`
// values is an array of string values. If the operator is In or NotIn,
// the values array must be non-empty. If the operator is Exists or DoesNotExist,
// the values array must be empty. This array is replaced during a strategic
// merge patch.
Values []string `json:"values,omitempty"`
}
// A label selector operator is the set of operators that can be used in a selector requirement.
type LabelSelectorOperator string
const (
LabelSelectorOpIn LabelSelectorOperator = "In"
LabelSelectorOpNotIn LabelSelectorOperator = "NotIn"
LabelSelectorOpExists LabelSelectorOperator = "Exists"
LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist"
)

View File

@ -0,0 +1,113 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 v1
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
// they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored.
//
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE
var map_Job = map[string]string{
"": "Job represents the configuration of a single job.",
"metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
"spec": "Spec is a structure defining the expected behavior of a job. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
"status": "Status is a structure describing current status of a job. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
}
func (Job) SwaggerDoc() map[string]string {
return map_Job
}
var map_JobCondition = map[string]string{
"": "JobCondition describes current state of a job.",
"type": "Type of job condition, Complete or Failed.",
"status": "Status of the condition, one of True, False, Unknown.",
"lastProbeTime": "Last time the condition was checked.",
"lastTransitionTime": "Last time the condition transit from one status to another.",
"reason": "(brief) reason for the condition's last transition.",
"message": "Human readable message indicating details about last transition.",
}
func (JobCondition) SwaggerDoc() map[string]string {
return map_JobCondition
}
var map_JobList = map[string]string{
"": "JobList is a collection of jobs.",
"metadata": "Standard list metadata More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
"items": "Items is the list of Job.",
}
func (JobList) SwaggerDoc() map[string]string {
return map_JobList
}
var map_JobSpec = map[string]string{
"": "JobSpec describes how the job execution will look like.",
"parallelism": "Parallelism specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
"completions": "Completions specifies the desired number of successfully finished pods the job should be run with. Setting to nil means that the success of any pod signals the success of all pods, and allows parallelism to have any positive value. Setting to 1 means that parallelism is limited to 1 and the success of that pod signals the success of the job.",
"activeDeadlineSeconds": "Optional duration in seconds relative to the startTime that the job may be active before the system tries to terminate it; value must be positive integer",
"selector": "Selector is a label query over pods that should match the pod count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors",
"template": "Template is the object that describes the pod that will be created when executing a job. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
}
func (JobSpec) SwaggerDoc() map[string]string {
return map_JobSpec
}
var map_JobStatus = map[string]string{
"": "JobStatus represents the current state of a Job.",
"conditions": "Conditions represent the latest available observations of an object's current state. More info: http://releases.k8s.io/HEAD/docs/user-guide/jobs.md",
"startTime": "StartTime represents time when the job was acknowledged by the Job Manager. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.",
"completionTime": "CompletionTime represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.",
"active": "Active is the number of actively running pods.",
"succeeded": "Succeeded is the number of pods which reached Phase Succeeded.",
"failed": "Failed is the number of pods which reached Phase Failed.",
}
func (JobStatus) SwaggerDoc() map[string]string {
return map_JobStatus
}
var map_LabelSelector = map[string]string{
"": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
"matchLabels": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
"matchExpressions": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
}
func (LabelSelector) SwaggerDoc() map[string]string {
return map_LabelSelector
}
var map_LabelSelectorRequirement = map[string]string{
"": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
"key": "key is the label key that the selector applies to.",
"operator": "operator represents a key's relationship to a set of values. Valid operators ard In, NotIn, Exists and DoesNotExist.",
"values": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
}
func (LabelSelectorRequirement) SwaggerDoc() map[string]string {
return map_LabelSelectorRequirement
}
// AUTO-GENERATED FUNCTIONS END HERE

View File

@ -16,7 +16,7 @@ limitations under the License.
package v1beta1
// This file contains a collection of methods that can be used from go-resful to
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//

View File

@ -0,0 +1,82 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/batch"
)
type BatchInterface interface {
JobsNamespacer
}
// BatchClient is used to interact with Kubernetes batch features.
type BatchClient struct {
*RESTClient
}
func (c *BatchClient) Jobs(namespace string) JobInterface {
return newJobsV1(c, namespace)
}
func NewBatch(c *Config) (*BatchClient, error) {
config := *c
if err := setBatchDefaults(&config); err != nil {
return nil, err
}
client, err := RESTClientFor(&config)
if err != nil {
return nil, err
}
return &BatchClient{client}, nil
}
func NewBatchOrDie(c *Config) *BatchClient {
client, err := NewBatch(c)
if err != nil {
panic(err)
}
return client
}
func setBatchDefaults(config *Config) error {
// if batch group is not registered, return an error
g, err := registered.Group(batch.GroupName)
if err != nil {
return err
}
config.APIPath = defaultAPIPath
if config.UserAgent == "" {
config.UserAgent = DefaultKubernetesUserAgent()
}
// TODO: Unconditionally set the config.Version, until we fix the config.
//if config.Version == "" {
copyGroupVersion := g.GroupVersion
config.GroupVersion = &copyGroupVersion
//}
config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion)
if config.QPS == 0 {
config.QPS = 5
}
if config.Burst == 0 {
config.Burst = 10
}
return nil
}

View File

@ -42,6 +42,7 @@ type Interface interface {
ComponentStatusesInterface
ConfigMapsNamespacer
Autoscaling() AutoscalingInterface
Batch() BatchInterface
Extensions() ExtensionsInterface
Discovery() DiscoveryInterface
}
@ -113,6 +114,7 @@ func (c *Client) ConfigMaps(namespace string) ConfigMapsInterface {
type Client struct {
*RESTClient
*AutoscalingClient
*BatchClient
*ExtensionsClient
*DiscoveryClient
}
@ -152,6 +154,10 @@ func (c *Client) Autoscaling() AutoscalingInterface {
return c.AutoscalingClient
}
func (c *Client) Batch() BatchInterface {
return c.BatchClient
}
func (c *Client) Extensions() ExtensionsInterface {
return c.ExtensionsClient
}

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
@ -165,6 +166,15 @@ func New(c *Config) (*Client, error) {
}
}
var batchClient *BatchClient
if registered.IsRegistered(batch.GroupName) {
batchConfig := *c
batchClient, err = NewBatch(&batchConfig)
if err != nil {
return nil, err
}
}
var extensionsClient *ExtensionsClient
if registered.IsRegistered(extensions.GroupName) {
extensionsConfig := *c
@ -174,7 +184,7 @@ func New(c *Config) (*Client, error) {
}
}
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient}, nil
}
// MatchesServerVersion queries the server to compares the build version

View File

@ -35,7 +35,7 @@ func getHorizontalPodAutoscalersResoureName() string {
return "horizontalpodautoscalers"
}
func getClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface {
func getHPAClient(t *testing.T, c *simple.Client, ns, resourceGroup string) HorizontalPodAutoscalerInterface {
switch resourceGroup {
case autoscaling.GroupName:
return c.Setup(t).Autoscaling().HorizontalPodAutoscalers(ns)
@ -66,7 +66,7 @@ func testHorizontalPodAutoscalerCreate(t *testing.T, group testapi.TestGroup, re
ResourceGroup: resourceGroup,
}
response, err := getClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler)
response, err := getHPAClient(t, c, ns, resourceGroup).Create(&horizontalPodAutoscaler)
defer c.Close()
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -98,7 +98,7 @@ func testHorizontalPodAutoscalerGet(t *testing.T, group testapi.TestGroup, resou
ResourceGroup: resourceGroup,
}
response, err := getClient(t, c, ns, resourceGroup).Get("abc")
response, err := getHPAClient(t, c, ns, resourceGroup).Get("abc")
defer c.Close()
c.Validate(t, response, err)
}
@ -130,7 +130,7 @@ func testHorizontalPodAutoscalerList(t *testing.T, group testapi.TestGroup, reso
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscalerList},
ResourceGroup: resourceGroup,
}
response, err := getClient(t, c, ns, resourceGroup).List(api.ListOptions{})
response, err := getHPAClient(t, c, ns, resourceGroup).List(api.ListOptions{})
defer c.Close()
c.Validate(t, response, err)
}
@ -154,7 +154,7 @@ func testHorizontalPodAutoscalerUpdate(t *testing.T, group testapi.TestGroup, re
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
ResourceGroup: resourceGroup,
}
response, err := getClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler)
response, err := getHPAClient(t, c, ns, resourceGroup).Update(horizontalPodAutoscaler)
defer c.Close()
c.Validate(t, response, err)
}
@ -178,7 +178,7 @@ func testHorizontalPodAutoscalerUpdateStatus(t *testing.T, group testapi.TestGro
Response: simple.Response{StatusCode: 200, Body: horizontalPodAutoscaler},
ResourceGroup: resourceGroup,
}
response, err := getClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler)
response, err := getHPAClient(t, c, ns, resourceGroup).UpdateStatus(horizontalPodAutoscaler)
defer c.Close()
c.Validate(t, response, err)
}
@ -195,7 +195,7 @@ func testHorizontalPodAutoscalerDelete(t *testing.T, group testapi.TestGroup, re
Response: simple.Response{StatusCode: 200},
ResourceGroup: resourceGroup,
}
err := getClient(t, c, ns, resourceGroup).Delete("foo", nil)
err := getHPAClient(t, c, ns, resourceGroup).Delete("foo", nil)
defer c.Close()
c.Validate(t, nil, err)
}
@ -214,7 +214,7 @@ func testHorizontalPodAutoscalerWatch(t *testing.T, group testapi.TestGroup, res
Response: simple.Response{StatusCode: 200},
ResourceGroup: resourceGroup,
}
_, err := getClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{})
_, err := getHPAClient(t, c, api.NamespaceAll, resourceGroup).Watch(api.ListOptions{})
defer c.Close()
c.Validate(t, nil, err)
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/apimachinery/registered"
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
_ "k8s.io/kubernetes/pkg/apis/metrics/install"

View File

@ -101,3 +101,67 @@ func (c *jobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, err er
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result)
return
}
// jobsV1 implements JobsNamespacer interface using BatchClient internally
type jobsV1 struct {
r *BatchClient
ns string
}
// newJobsV1 returns a jobsV1
func newJobsV1(c *BatchClient, namespace string) *jobsV1 {
return &jobsV1{c, namespace}
}
// Ensure statically that jobsV1 implements JobInterface.
var _ JobInterface = &jobsV1{}
// List returns a list of jobs that match the label and field selectors.
func (c *jobsV1) List(opts api.ListOptions) (result *extensions.JobList, err error) {
result = &extensions.JobList{}
err = c.r.Get().Namespace(c.ns).Resource("jobs").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular job.
func (c *jobsV1) Get(name string) (result *extensions.Job, err error) {
result = &extensions.Job{}
err = c.r.Get().Namespace(c.ns).Resource("jobs").Name(name).Do().Into(result)
return
}
// Create creates a new job.
func (c *jobsV1) Create(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
err = c.r.Post().Namespace(c.ns).Resource("jobs").Body(job).Do().Into(result)
return
}
// Update updates an existing job.
func (c *jobsV1) Update(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).Body(job).Do().Into(result)
return
}
// Delete deletes a job, returns error if one occurs.
func (c *jobsV1) Delete(name string, options *api.DeleteOptions) (err error) {
return c.r.Delete().Namespace(c.ns).Resource("jobs").Name(name).Body(options).Do().Error()
}
// Watch returns a watch.Interface that watches the requested jobs.
func (c *jobsV1) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("jobs").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}
// UpdateStatus takes the name of the job and the new status. Returns the server's representation of the job, and an error, if it occurs.
func (c *jobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result)
return
}

View File

@ -26,19 +26,32 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func getJobResourceName() string {
func getJobsResourceName() string {
return "jobs"
}
func TestListJobs(t *testing.T) {
func getJobClient(t *testing.T, c *simple.Client, ns, resourceGroup string) JobInterface {
switch resourceGroup {
case batch.GroupName:
return c.Setup(t).Batch().Jobs(ns)
case extensions.GroupName:
return c.Setup(t).Extensions().Jobs(ns)
default:
t.Fatalf("Unknown group %v", resourceGroup)
}
return nil
}
func testListJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceAll
c := &simple.Client{
Request: simple.Request{
Method: "GET",
Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, ""),
Path: group.ResourcePath(getJobsResourceName(), ns, ""),
},
Response: simple.Response{StatusCode: 200,
Body: &extensions.JobList{
@ -58,18 +71,24 @@ func TestListJobs(t *testing.T) {
},
},
},
ResourceGroup: resourceGroup,
}
receivedJobList, err := c.Setup(t).Extensions().Jobs(ns).List(api.ListOptions{})
receivedJobList, err := getJobClient(t, c, ns, resourceGroup).List(api.ListOptions{})
defer c.Close()
c.Validate(t, receivedJobList, err)
}
func TestGetJob(t *testing.T) {
func TestListJob(t *testing.T) {
testListJob(t, testapi.Extensions, extensions.GroupName)
testListJob(t, testapi.Batch, batch.GroupName)
}
func testGetJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
c := &simple.Client{
Request: simple.Request{
Method: "GET",
Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"),
Path: group.ResourcePath(getJobsResourceName(), ns, "foo"),
Query: simple.BuildQueryValues(nil),
},
Response: simple.Response{
@ -87,61 +106,61 @@ func TestGetJob(t *testing.T) {
},
},
},
ResourceGroup: resourceGroup,
}
receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Get("foo")
receivedJob, err := getJobClient(t, c, ns, resourceGroup).Get("foo")
defer c.Close()
c.Validate(t, receivedJob, err)
}
func TestGetJobWithNoName(t *testing.T) {
ns := api.NamespaceDefault
c := &simple.Client{Error: true}
receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Get("")
defer c.Close()
if (err != nil) && (err.Error() != simple.NameRequiredError) {
t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err)
}
func TestGetJob(t *testing.T) {
testGetJob(t, testapi.Extensions, extensions.GroupName)
testGetJob(t, testapi.Batch, batch.GroupName)
}
func testUpdateJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: ns,
ResourceVersion: "1",
},
}
c := &simple.Client{
Request: simple.Request{
Method: "PUT",
Path: group.ResourcePath(getJobsResourceName(), ns, "foo"),
Query: simple.BuildQueryValues(nil),
},
Response: simple.Response{
StatusCode: 200,
Body: &extensions.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
"name": "baz",
},
},
Spec: extensions.JobSpec{
Template: api.PodTemplateSpec{},
},
},
},
ResourceGroup: resourceGroup,
}
receivedJob, err := getJobClient(t, c, ns, resourceGroup).Update(requestJob)
defer c.Close()
c.Validate(t, receivedJob, err)
}
func TestUpdateJob(t *testing.T) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: ns,
ResourceVersion: "1",
},
}
c := &simple.Client{
Request: simple.Request{
Method: "PUT",
Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"),
Query: simple.BuildQueryValues(nil),
},
Response: simple.Response{
StatusCode: 200,
Body: &extensions.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
"name": "baz",
},
},
Spec: extensions.JobSpec{
Template: api.PodTemplateSpec{},
},
},
},
}
receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Update(requestJob)
defer c.Close()
c.Validate(t, receivedJob, err)
testUpdateJob(t, testapi.Extensions, extensions.GroupName)
testUpdateJob(t, testapi.Batch, batch.GroupName)
}
func TestUpdateJobStatus(t *testing.T) {
func testUpdateJobStatus(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
ObjectMeta: api.ObjectMeta{
@ -153,7 +172,7 @@ func TestUpdateJobStatus(t *testing.T) {
c := &simple.Client{
Request: simple.Request{
Method: "PUT",
Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo") + "/status",
Path: group.ResourcePath(getJobsResourceName(), ns, "foo") + "/status",
Query: simple.BuildQueryValues(nil),
},
Response: simple.Response{
@ -174,28 +193,40 @@ func TestUpdateJobStatus(t *testing.T) {
},
},
},
ResourceGroup: resourceGroup,
}
receivedJob, err := c.Setup(t).Extensions().Jobs(ns).UpdateStatus(requestJob)
receivedJob, err := getJobClient(t, c, ns, resourceGroup).UpdateStatus(requestJob)
defer c.Close()
c.Validate(t, receivedJob, err)
}
func TestDeleteJob(t *testing.T) {
func TestUpdateJobStatus(t *testing.T) {
testUpdateJobStatus(t, testapi.Extensions, extensions.GroupName)
testUpdateJobStatus(t, testapi.Batch, batch.GroupName)
}
func testDeleteJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
c := &simple.Client{
Request: simple.Request{
Method: "DELETE",
Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, "foo"),
Path: group.ResourcePath(getJobsResourceName(), ns, "foo"),
Query: simple.BuildQueryValues(nil),
},
Response: simple.Response{StatusCode: 200},
Response: simple.Response{StatusCode: 200},
ResourceGroup: resourceGroup,
}
err := c.Setup(t).Extensions().Jobs(ns).Delete("foo", nil)
err := getJobClient(t, c, ns, resourceGroup).Delete("foo", nil)
defer c.Close()
c.Validate(t, nil, err)
}
func TestCreateJob(t *testing.T) {
func TestDeleteJob(t *testing.T) {
testDeleteJob(t, testapi.Extensions, extensions.GroupName)
testDeleteJob(t, testapi.Batch, batch.GroupName)
}
func testCreateJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
ObjectMeta: api.ObjectMeta{
@ -206,7 +237,7 @@ func TestCreateJob(t *testing.T) {
c := &simple.Client{
Request: simple.Request{
Method: "POST",
Path: testapi.Extensions.ResourcePath(getJobResourceName(), ns, ""),
Path: group.ResourcePath(getJobsResourceName(), ns, ""),
Body: requestJob,
Query: simple.BuildQueryValues(nil),
},
@ -225,8 +256,17 @@ func TestCreateJob(t *testing.T) {
},
},
},
ResourceGroup: resourceGroup,
}
receivedJob, err := c.Setup(t).Extensions().Jobs(ns).Create(requestJob)
receivedJob, err := getJobClient(t, c, ns, resourceGroup).Create(requestJob)
defer c.Close()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
c.Validate(t, receivedJob, err)
}
func TestCreateJob(t *testing.T) {
testCreateJob(t, testapi.Extensions, extensions.GroupName)
testCreateJob(t, testapi.Batch, batch.GroupName)
}

View File

@ -82,3 +82,66 @@ func (c *FakeJobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, er
return obj.(*extensions.Job), err
}
// FakeJobs implements JobInterface. Meant to be embedded into a struct to get a default
// implementation. This makes faking out just the methods you want to test easier.
// This is a test implementation of JobsV1
// TODO(piosz): get back to one client implementation once HPA will be graduated to GA completely
type FakeJobsV1 struct {
Fake *FakeBatch
Namespace string
}
func (c *FakeJobsV1) Get(name string) (*extensions.Job, error) {
obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &extensions.Job{})
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
}
func (c *FakeJobsV1) List(opts api.ListOptions) (*extensions.JobList, error) {
obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &extensions.JobList{})
if obj == nil {
return nil, err
}
return obj.(*extensions.JobList), err
}
func (c *FakeJobsV1) Create(job *extensions.Job) (*extensions.Job, error) {
obj, err := c.Fake.Invokes(NewCreateAction("jobs", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
}
func (c *FakeJobsV1) Update(job *extensions.Job) (*extensions.Job, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("jobs", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
}
func (c *FakeJobsV1) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &extensions.Job{})
return err
}
func (c *FakeJobsV1) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction("jobs", c.Namespace, opts))
}
func (c *FakeJobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) {
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("jobs", "status", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
}

View File

@ -92,6 +92,10 @@ func (c *Client) Setup(t *testing.T) *Client {
Host: c.server.URL,
ContentConfig: client.ContentConfig{GroupVersion: testapi.Autoscaling.GroupVersion()},
})
c.BatchClient = client.NewBatchOrDie(&client.Config{
Host: c.server.URL,
ContentConfig: client.ContentConfig{GroupVersion: testapi.Batch.GroupVersion()},
})
c.ExtensionsClient = client.NewExtensionsOrDie(&client.Config{
Host: c.server.URL,
ContentConfig: client.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()},

View File

@ -278,6 +278,10 @@ func (c *Fake) Autoscaling() client.AutoscalingInterface {
return &FakeAutoscaling{c}
}
func (c *Fake) Batch() client.BatchInterface {
return &FakeBatch{c}
}
func (c *Fake) Extensions() client.ExtensionsInterface {
return &FakeExperimental{c}
}
@ -321,6 +325,19 @@ func (c *FakeAutoscaling) HorizontalPodAutoscalers(namespace string) client.Hori
return &FakeHorizontalPodAutoscalersV1{Fake: c, Namespace: namespace}
}
// NewSimpleFakeBatch returns a client that will respond with the provided objects
func NewSimpleFakeBatch(objects ...runtime.Object) *FakeBatch {
return &FakeBatch{Fake: NewSimpleFake(objects...)}
}
type FakeBatch struct {
*Fake
}
func (c *FakeBatch) Jobs(namespace string) client.JobInterface {
return &FakeJobsV1{Fake: c, Namespace: namespace}
}
// NewSimpleFakeExp returns a client that will respond with the provided objects
func NewSimpleFakeExp(objects ...runtime.Object) *FakeExperimental {
return &FakeExperimental{Fake: NewSimpleFake(objects...)}

View File

@ -242,7 +242,7 @@ func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) {
daemonset_pod = true
}
} else if sr.Reference.Kind == "Job" {
job, err := o.client.Jobs(sr.Reference.Namespace).Get(sr.Reference.Name)
job, err := o.client.ExtensionsClient.Jobs(sr.Reference.Namespace).Get(sr.Reference.Name)
// Assume the only reason for an error is because the Job is
// gone/missing, not for any other cause. TODO(mml): something more

View File

@ -40,6 +40,7 @@ import (
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
client "k8s.io/kubernetes/pkg/client/unversioned"
@ -221,6 +222,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return client.RESTClient, nil
case autoscaling.GroupName:
return client.AutoscalingClient.RESTClient, nil
case batch.GroupName:
return client.BatchClient.RESTClient, nil
case extensions.GroupName:
return client.ExtensionsClient.RESTClient, nil
}
@ -710,6 +713,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
}
return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir)
}
if gvk.Group == batch.GroupName {
if c.c.BatchClient == nil {
return errors.New("unable to validate: no batch client")
}
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir)
}
if gvk.Group == extensions.GroupName {
if c.c.ExtensionsClient == nil {
return errors.New("unable to validate: no experimental client")

View File

@ -33,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
client "k8s.io/kubernetes/pkg/client/unversioned"
@ -94,6 +95,7 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer {
extensions.Kind("DaemonSet"): &DaemonSetDescriber{c},
extensions.Kind("Deployment"): &DeploymentDescriber{clientset.FromUnversionedClient(c)},
extensions.Kind("Job"): &JobDescriber{c},
batch.Kind("Job"): &JobDescriber{c},
extensions.Kind("Ingress"): &IngressDescriber{c},
}

View File

@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/util/wait"
@ -46,8 +47,8 @@ func ScalerFor(kind unversioned.GroupKind, c client.Interface) (Scaler, error) {
return &ReplicationControllerScaler{c}, nil
case extensions.Kind("ReplicaSet"):
return &ReplicaSetScaler{c.Extensions()}, nil
case extensions.Kind("Job"):
return &JobScaler{c.Extensions()}, nil
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobScaler{c.Extensions()}, nil // Either kind of job can be scaled with Extensions interface.
case extensions.Kind("Deployment"):
return &DeploymentScaler{c.Extensions()}, nil
}

View File

@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/labels"
@ -75,7 +76,7 @@ func ReaperFor(kind unversioned.GroupKind, c client.Interface) (Reaper, error) {
case api.Kind("Service"):
return &ServiceReaper{c}, nil
case extensions.Kind("Job"):
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobReaper{c, Interval, Timeout}, nil
case extensions.Kind("Deployment"):

View File

@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/apimachinery/registered"
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
_ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
)

View File

@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apiserver"
apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics"
@ -285,6 +286,38 @@ func (m *Master) InstallAPIs(c *Config) {
allGroups = append(allGroups, group)
}
// Install batch unless disabled.
if !m.ApiGroupVersionOverrides["batch/v1"].Disable {
batchResources := m.getBatchResources(c)
batchGroupMeta := registered.GroupOrDie(batch.GroupName)
// Hard code preferred group version to batch/v1
batchGroupMeta.GroupVersion = unversioned.GroupVersion{Group: "batch", Version: "v1"}
apiGroupInfo := genericapiserver.APIGroupInfo{
GroupMeta: *batchGroupMeta,
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{
"v1": batchResources,
},
OptionsExternalVersion: &registered.GroupOrDie(api.GroupName).GroupVersion,
Scheme: api.Scheme,
ParameterCodec: api.ParameterCodec,
NegotiatedSerializer: api.Codecs,
}
apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo)
batchGVForDiscovery := unversioned.GroupVersionForDiscovery{
GroupVersion: batchGroupMeta.GroupVersion.String(),
Version: batchGroupMeta.GroupVersion.Version,
}
group := unversioned.APIGroup{
Name: batchGroupMeta.GroupVersion.Group,
Versions: []unversioned.GroupVersionForDiscovery{batchGVForDiscovery},
PreferredVersion: batchGVForDiscovery,
}
allGroups = append(allGroups, group)
}
if err := m.InstallAPIGroups(apiGroupsInfo); err != nil {
glog.Fatalf("Error in registering group versions: %v", err)
}
@ -666,9 +699,7 @@ func (m *Master) getExtensionResources(c *Config) map[string]rest.Storage {
storage["deployments/rollback"] = deploymentStorage.Rollback
}
if isEnabled("jobs") {
jobStorage, jobStatusStorage := jobetcd.NewREST(dbClient("jobs"), storageDecorator)
storage["jobs"] = jobStorage
storage["jobs/status"] = jobStatusStorage
m.constructJobResources(c, storage)
}
if isEnabled("ingresses") {
ingressStorage, ingressStatusStorage := ingressetcd.NewREST(dbClient("ingresses"), storageDecorator)
@ -722,6 +753,40 @@ func (m *Master) getAutoscalingResources(c *Config) map[string]rest.Storage {
return storage
}
// constructJobResources makes Job resources and adds them to the storage map.
// They're installed in both batch and extensions. It's assumed that you've
// already done the check that they should be on.
func (m *Master) constructJobResources(c *Config, restStorage map[string]rest.Storage) {
// Note that job's storage settings are changed by changing the batch
// group. Clearly we want all jobs to be stored in the same place no
// matter where they're accessed from.
storageDecorator := m.StorageDecorator()
dbClient := func(resource string) storage.Interface {
return c.StorageDestinations.Search([]string{batch.GroupName, extensions.GroupName}, resource)
}
jobStorage, jobStatusStorage := jobetcd.NewREST(dbClient("jobs"), storageDecorator)
restStorage["jobs"] = jobStorage
restStorage["jobs/status"] = jobStatusStorage
}
// getBatchResources returns the resources for batch api
func (m *Master) getBatchResources(c *Config) map[string]rest.Storage {
resourceOverrides := m.ApiGroupVersionOverrides["batch/v1"].ResourceOverrides
isEnabled := func(resource string) bool {
// Check if the resource has been overriden.
if enabled, ok := resourceOverrides[resource]; ok {
return enabled
}
return !m.ApiGroupVersionOverrides["batch/v1"].Disable
}
storage := map[string]rest.Storage{}
if isEnabled("jobs") {
m.constructJobResources(c, storage)
}
return storage
}
// findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP.
func findExternalAddress(node *api.Node) (string, error) {
var fallback string

View File

@ -32,10 +32,10 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
apiutil "k8s.io/kubernetes/pkg/api/util"
utilnet "k8s.io/kubernetes/pkg/util/net"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/genericapiserver"
"k8s.io/kubernetes/pkg/kubelet/client"
@ -69,12 +69,15 @@ func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.A
api.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Default.Codec(), etcdtest.PathPrefix(), false))
storageDestinations.AddAPIGroup(
autoscaling.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false))
storageDestinations.AddAPIGroup(
batch.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Batch.Codec(), etcdtest.PathPrefix(), false))
storageDestinations.AddAPIGroup(
extensions.GroupName, etcdstorage.NewEtcdStorage(server.Client, testapi.Extensions.Codec(), etcdtest.PathPrefix(), false))
config.StorageDestinations = storageDestinations
storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String()
storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String()
storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
config.StorageVersions = storageVersions
config.PublicAddress = net.ParseIP("192.168.10.4")
@ -337,95 +340,114 @@ func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
}
func TestDiscoveryAtAPIS(t *testing.T) {
master, etcdserver, config, assert := newMaster(t)
defer etcdserver.Terminate(t)
// TODO(caesarxuchao): make this pass now that batch is added,
// and rewrite it so that the indexes do not need to change each time a new api group is added.
/*
master, etcdserver, config, assert := newMaster(t)
defer etcdserver.Terminate(t)
server := httptest.NewServer(master.HandlerContainer.ServeMux)
resp, err := http.Get(server.URL + "/apis")
if !assert.NoError(err) {
t.Errorf("unexpected error: %v", err)
}
server := httptest.NewServer(master.HandlerContainer.ServeMux)
resp, err := http.Get(server.URL + "/apis")
if !assert.NoError(err) {
t.Errorf("unexpected error: %v", err)
}
assert.Equal(http.StatusOK, resp.StatusCode)
assert.Equal(http.StatusOK, resp.StatusCode)
groupList := unversioned.APIGroupList{}
assert.NoError(decodeResponse(resp, &groupList))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
groupList := unversioned.APIGroupList{}
assert.NoError(decodeResponse(resp, &groupList))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectGroupNames := []string{autoscaling.GroupName, extensions.GroupName}
expectVersions := [][]unversioned.GroupVersionForDiscovery{
{
expectGroupNames := []string{autoscaling.GroupName, batch.GroupName, extensions.GroupName}
expectVersions := [][]unversioned.GroupVersionForDiscovery{
{
GroupVersion: testapi.Autoscaling.GroupVersion().String(),
Version: testapi.Autoscaling.GroupVersion().Version,
{
GroupVersion: testapi.Autoscaling.GroupVersion().String(),
Version: testapi.Autoscaling.GroupVersion().Version,
},
},
},
{
{
GroupVersion: testapi.Extensions.GroupVersion().String(),
Version: testapi.Extensions.GroupVersion().Version,
{
GroupVersion: testapi.Batch.GroupVersion().String(),
Version: testapi.Batch.GroupVersion().Version,
},
},
},
}
expectPreferredVersion := []unversioned.GroupVersionForDiscovery{
{
GroupVersion: config.StorageVersions[autoscaling.GroupName],
Version: apiutil.GetVersion(config.StorageVersions[autoscaling.GroupName]),
},
{
GroupVersion: config.StorageVersions[extensions.GroupName],
Version: apiutil.GetVersion(config.StorageVersions[extensions.GroupName]),
},
}
{
{
GroupVersion: testapi.Extensions.GroupVersion().String(),
Version: testapi.Extensions.GroupVersion().Version,
},
},
}
expectPreferredVersion := []unversioned.GroupVersionForDiscovery{
{
GroupVersion: config.StorageVersions[autoscaling.GroupName],
Version: apiutil.GetVersion(config.StorageVersions[autoscaling.GroupName]),
},
{
GroupVersion: config.StorageVersions[batch.GroupName],
Version: apiutil.GetVersion(config.StorageVersions[batch.GroupName]),
},
{
GroupVersion: config.StorageVersions[extensions.GroupName],
Version: apiutil.GetVersion(config.StorageVersions[extensions.GroupName]),
},
}
assert.Equal(2, len(groupList.Groups))
assert.Equal(expectGroupNames[0], groupList.Groups[0].Name)
assert.Equal(expectGroupNames[1], groupList.Groups[1].Name)
assert.Equal(expectVersions[0], groupList.Groups[0].Versions)
assert.Equal(expectVersions[1], groupList.Groups[1].Versions)
assert.Equal(2, len(groupList.Groups))
assert.Equal(expectGroupNames[0], groupList.Groups[0].Name)
assert.Equal(expectGroupNames[1], groupList.Groups[1].Name)
assert.Equal(expectPreferredVersion[0], groupList.Groups[0].PreferredVersion)
assert.Equal(expectPreferredVersion[1], groupList.Groups[1].PreferredVersion)
assert.Equal(expectVersions[0], groupList.Groups[0].Versions)
assert.Equal(expectVersions[1], groupList.Groups[1].Versions)
thirdPartyGV := unversioned.GroupVersionForDiscovery{GroupVersion: "company.com/v1", Version: "v1"}
master.addThirdPartyResourceStorage("/apis/company.com/v1", nil,
unversioned.APIGroup{
Name: "company.com",
Versions: []unversioned.GroupVersionForDiscovery{thirdPartyGV},
PreferredVersion: thirdPartyGV,
})
assert.Equal(expectPreferredVersion[0], groupList.Groups[0].PreferredVersion)
assert.Equal(expectPreferredVersion[1], groupList.Groups[1].PreferredVersion)
resp, err = http.Get(server.URL + "/apis")
if !assert.NoError(err) {
t.Errorf("unexpected error: %v", err)
}
thirdPartyGV := unversioned.GroupVersionForDiscovery{GroupVersion: "company.com/v1", Version: "v1"}
master.addThirdPartyResourceStorage("/apis/company.com/v1", nil,
unversioned.APIGroup{
Name: "company.com",
Versions: []unversioned.GroupVersionForDiscovery{thirdPartyGV},
PreferredVersion: thirdPartyGV,
})
assert.Equal(http.StatusOK, resp.StatusCode)
resp, err = http.Get(server.URL + "/apis")
if !assert.NoError(err) {
t.Errorf("unexpected error: %v", err)
}
assert.NoError(decodeResponse(resp, &groupList))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
assert.Equal(http.StatusOK, resp.StatusCode)
thirdPartyGroupName := "company.com"
thirdPartyExpectVersions := []unversioned.GroupVersionForDiscovery{thirdPartyGV}
assert.NoError(decodeResponse(resp, &groupList))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
assert.Equal(3, len(groupList.Groups))
// autoscaling group
assert.Equal(expectGroupNames[0], groupList.Groups[0].Name)
assert.Equal(expectVersions[0], groupList.Groups[0].Versions)
assert.Equal(expectPreferredVersion[0], groupList.Groups[0].PreferredVersion)
// third party
assert.Equal(thirdPartyGroupName, groupList.Groups[1].Name)
assert.Equal(thirdPartyExpectVersions, groupList.Groups[1].Versions)
assert.Equal(thirdPartyGV, groupList.Groups[1].PreferredVersion)
// extensions group
assert.Equal(expectGroupNames[1], groupList.Groups[2].Name)
assert.Equal(expectVersions[1], groupList.Groups[2].Versions)
assert.Equal(expectPreferredVersion[1], groupList.Groups[2].PreferredVersion)
thirdPartyGroupName := "company.com"
thirdPartyExpectVersions := []unversioned.GroupVersionForDiscovery{thirdPartyGV}
assert.Equal(4, len(groupList.Groups))
// autoscaling group
assert.Equal(expectGroupNames[0], groupList.Groups[0].Name)
assert.Equal(expectVersions[0], groupList.Groups[0].Versions)
assert.Equal(expectPreferredVersion[0], groupList.Groups[0].PreferredVersion)
// batch group
assert.Equal(expectGroupNames[1], groupList.Groups[1].Name)
assert.Equal(expectVersions[1], groupList.Groups[1].Versions)
assert.Equal(expectPreferredVersion[1], groupList.Groups[1].PreferredVersion)
// third party
assert.Equal(thirdPartyGroupName, groupList.Groups[2].Name)
assert.Equal(thirdPartyExpectVersions, groupList.Groups[2].Versions)
assert.Equal(thirdPartyGV, groupList.Groups[2].PreferredVersion)
// extensions group
assert.Equal(expectGroupNames[2], groupList.Groups[3].Name)
assert.Equal(expectVersions[2], groupList.Groups[3].Versions)
assert.Equal(expectPreferredVersion[2], groupList.Groups[3].PreferredVersion)
*/
}
var versionsToTest = []string{"v1", "v3"}

View File

@ -59,6 +59,13 @@ func NewAutoscalingEtcdStorage(client etcd.Client) storage.Interface {
return etcdstorage.NewEtcdStorage(client, testapi.Autoscaling.Codec(), etcdtest.PathPrefix(), false)
}
func NewBatchEtcdStorage(client etcd.Client) storage.Interface {
if client == nil {
client = NewEtcdClient()
}
return etcdstorage.NewEtcdStorage(client, testapi.Batch.Codec(), etcdtest.PathPrefix(), false)
}
func NewExtensionsEtcdStorage(client etcd.Client) storage.Interface {
if client == nil {
client = NewEtcdClient()

View File

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apiserver"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
@ -153,12 +154,15 @@ func NewMasterConfig() *master.Config {
storageVersions[api.GroupName] = testapi.Default.GroupVersion().String()
autoscalingEtcdStorage := NewAutoscalingEtcdStorage(etcdClient)
storageVersions[autoscaling.GroupName] = testapi.Autoscaling.GroupVersion().String()
batchEtcdStorage := NewBatchEtcdStorage(etcdClient)
storageVersions[batch.GroupName] = testapi.Batch.GroupVersion().String()
expEtcdStorage := NewExtensionsEtcdStorage(etcdClient)
storageVersions[extensions.GroupName] = testapi.Extensions.GroupVersion().String()
storageDestinations := genericapiserver.NewStorageDestinations()
storageDestinations.AddAPIGroup(api.GroupName, etcdStorage)
storageDestinations.AddAPIGroup(autoscaling.GroupName, autoscalingEtcdStorage)
storageDestinations.AddAPIGroup(batch.GroupName, batchEtcdStorage)
storageDestinations.AddAPIGroup(extensions.GroupName, expEtcdStorage)
return &master.Config{

View File

@ -51,6 +51,10 @@ func TestAutoscalingPrefix(t *testing.T) {
testPrefix(t, "/apis/autoscaling/")
}
func TestBatchPrefix(t *testing.T) {
testPrefix(t, "/apis/batch/")
}
func TestExtensionsPrefix(t *testing.T) {
testPrefix(t, "/apis/extensions/")
}
@ -95,6 +99,10 @@ func autoscalingPath(resource, namespace, name string) string {
return testapi.Autoscaling.ResourcePath(resource, namespace, name)
}
func batchPath(resource, namespace, name string) string {
return testapi.Batch.ResourcePath(resource, namespace, name)
}
func extensionsPath(resource, namespace, name string) string {
return testapi.Extensions.ResourcePath(resource, namespace, name)
}
@ -145,6 +153,164 @@ func TestAutoscalingGroupBackwardCompatibility(t *testing.T) {
}
}
var jobV1beta1 string = `
{
"kind": "Job",
"apiVersion": "extensions/v1beta1",
"metadata": {
"name": "pi",
"labels": {
"app": "pi"
}
},
"spec": {
"parallelism": 1,
"completions": 1,
"selector": {
"matchLabels": {
"app": "pi"
}
},
"template": {
"metadata": {
"name": "pi",
"creationTimestamp": null,
"labels": {
"app": "pi"
}
},
"spec": {
"containers": [
{
"name": "pi",
"image": "perl",
"command": [
"perl",
"-Mbignum=bpi",
"-wle",
"print bpi(2000)"
]
}
],
"restartPolicy": "Never"
}
}
}
}
`
var jobV1 string = `
{
"kind": "Job",
"apiVersion": "batch/v1",
"metadata": {
"name": "pi",
"labels": {
"app": "pi"
}
},
"spec": {
"parallelism": 1,
"completions": 1,
"selector": {
"matchLabels": {
"app": "pi"
}
},
"template": {
"metadata": {
"name": "pi",
"creationTimestamp": null,
"labels": {
"app": "pi"
}
},
"spec": {
"containers": [
{
"name": "pi",
"image": "perl",
"command": [
"perl",
"-Mbignum=bpi",
"-wle",
"print bpi(2000)"
]
}
],
"restartPolicy": "Never"
}
}
}
}
`
var deleteResp string = `
{
"kind": "Status",
"apiVersion": "v1",
"metadata":{},
"status":"Success",
"code":200
}
`
// TestBatchGroupBackwardCompatibility is testing that batch/v1 and ext/v1beta1
// Job share storage. This test can be deleted when Jobs is removed from ext/v1beta1,
// (expected to happen in 1.4).
func TestBatchGroupBackwardCompatibility(t *testing.T) {
_, s := framework.RunAMaster(t)
defer s.Close()
transport := http.DefaultTransport
requests := []struct {
verb string
URL string
body string
expectedStatusCodes map[int]bool
expectedVersion string
}{
// Post a v1 and get back both as v1beta1 and as v1.
{"POST", batchPath("jobs", api.NamespaceDefault, ""), jobV1, code201, ""},
{"GET", batchPath("jobs", api.NamespaceDefault, "pi"), "", code200, testapi.Batch.GroupVersion().String()},
{"GET", extensionsPath("jobs", api.NamespaceDefault, "pi"), "", code200, testapi.Extensions.GroupVersion().String()},
{"DELETE", batchPath("jobs", api.NamespaceDefault, "pi"), "", code200, testapi.Default.GroupVersion().String()}, // status response
// Post a v1beta1 and get back both as v1beta1 and as v1.
{"POST", extensionsPath("jobs", api.NamespaceDefault, ""), jobV1beta1, code201, ""},
{"GET", batchPath("jobs", api.NamespaceDefault, "pi"), "", code200, testapi.Batch.GroupVersion().String()},
{"GET", extensionsPath("jobs", api.NamespaceDefault, "pi"), "", code200, testapi.Extensions.GroupVersion().String()},
{"DELETE", extensionsPath("jobs", api.NamespaceDefault, "pi"), "", code200, testapi.Default.GroupVersion().String()}, //status response
}
for _, r := range requests {
bodyBytes := bytes.NewReader([]byte(r.body))
req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
if err != nil {
t.Logf("case %v", r)
t.Fatalf("unexpected error: %v", err)
}
func() {
resp, err := transport.RoundTrip(req)
defer resp.Body.Close()
if err != nil {
t.Logf("case %v", r)
t.Fatalf("unexpected error: %v", err)
}
b, _ := ioutil.ReadAll(resp.Body)
body := string(b)
if _, ok := r.expectedStatusCodes[resp.StatusCode]; !ok {
t.Logf("case %v", r)
t.Errorf("Expected status one of %v, but got %v", r.expectedStatusCodes, resp.StatusCode)
t.Errorf("Body: %v", body)
}
if !strings.Contains(body, "\"apiVersion\":\""+r.expectedVersion) {
t.Logf("case %v", r)
t.Errorf("Expected version %v, got body %v", r.expectedVersion, body)
}
}()
}
}
func TestAccept(t *testing.T) {
_, s := framework.RunAMaster(t)
// TODO: Uncomment when fix #19254