Merge pull request #600 from mhenriks/manifest-gen

generate cdi-controller.yaml from resources defined in code
This commit is contained in:
Alexander Wels 2019-01-11 13:33:42 -05:00 committed by GitHub
commit 656f6fe221
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
192 changed files with 32422 additions and 552 deletions

11
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: 79d152fb584cb4e17e24b60927398c6ae580fce5625a80a6e284aeba8cb9e461
updated: 2019-01-08T21:10:02.533518921Z
hash: e0155c8be846dca64d527ca4805bdf6ea796ea8ccc7c19802f408995a7113cab
updated: 2019-01-09T16:19:14.466534746Z
imports:
- name: github.com/beorn7/perks
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
@ -145,7 +145,7 @@ imports:
- name: github.com/peterbourgon/diskv
version: 5f041e8faa004a95c88a202771f4cc3e991971e6
- name: github.com/pkg/errors
version: c38ea53d8c3f78ac85b9b0f1247abeb6d38f6988
version: ffb6e22f01932bf7ac35e0bad9be11f01d1c8685
- name: github.com/prometheus/client_golang
version: 505eaef017263e299324067d40ca2c48f6a2cf50
subpackages:
@ -307,6 +307,11 @@ imports:
- storage/v1
- storage/v1alpha1
- storage/v1beta1
- name: k8s.io/apiextensions-apiserver
version: 853f76028711219c2fc251fd5184f23fd44a7aa0
subpackages:
- pkg/apis/apiextensions
- pkg/apis/apiextensions/v1beta1
- name: k8s.io/apimachinery
version: 49ce2735e5074ffc3f8190c8406cf51a96302dad
subpackages:

View File

@ -30,3 +30,5 @@ import:
version: ^1.0.0
- package: sigs.k8s.io/controller-runtime
version: v0.1.9
- package: k8s.io/apiextensions-apiserver
version: kubernetes-1.12.4

View File

@ -18,7 +18,7 @@ script_dir="$(readlink -f $(dirname $0))"
source "${script_dir}"/common.sh
source "${script_dir}"/config.sh
templates="$(find "${MANIFEST_TEMPLATE_DIR}" -name *.in -type f)"
templates="$(find "${MANIFEST_TEMPLATE_DIR}" -name "*.in" -type f)"
generator="${BIN_DIR}/manifest-generator"
(cd "${CDI_DIR}/tools/manifest-generator/" && go build -o "${generator}" ./...)
@ -60,5 +60,33 @@ for tmpl in ${templates}; do
) 1>"${MANIFEST_GENERATED_DIR}/${outFile}.j2"
done
(${generator} -code-group=everything \
-docker-repo="${DOCKER_REPO}" \
-docker-tag="${DOCKER_TAG}" \
-controller-image="${CONTROLLER_IMAGE_NAME}" \
-importer-image="${IMPORTER_IMAGE_NAME}" \
-cloner-image="${CLONER_IMAGE_NAME}" \
-apiserver-image=${APISERVER_IMAGE_NAME} \
-uploadproxy-image=${UPLOADPROXY_IMAGE_NAME} \
-uploadserver-image=${UPLOADSERVER_IMAGE_NAME} \
-verbosity="${VERBOSITY}" \
-pull-policy="${PULL_POLICY}" \
-namespace="${NAMESPACE}"
) 1>"${MANIFEST_GENERATED_DIR}/cdi-controller.yaml"
(${generator} -code-group=everything \
-docker-repo="{{ docker_prefix }}" \
-docker-tag="{{ docker_tag }}" \
-controller-image="{{ controller_image }}" \
-importer-image="{{ importer_image }}" \
-cloner-image="{{ cloner_image }}" \
-apiserver-image="{{ apiserver_image }}" \
-uploadproxy-image="{{ uploadproxy_image }}" \
-uploadserver-image="{{ uploadserver_image }}" \
-verbosity="{{ verbosity }}" \
-pull-policy="{{ pull_policy }}" \
-namespace="{{ cdi_namespace }}"
) 1>"${MANIFEST_GENERATED_DIR}/cdi-controller.yaml.j2"
# Remove empty lines at the end of files which are added by go templating
find ${MANIFEST_GENERATED_DIR}/ -type f -exec sed -i {} -e '${/^$/d;}' \;

View File

@ -1,451 +0,0 @@
kind: ServiceAccount
apiVersion: v1
metadata:
name: cdi-sa
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: ""
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cdi
labels:
cdi.kubevirt.io: ""
rules:
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims/finalizers"]
verbs: ["update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["pods/finalizers"]
verbs: ["update"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch", "create"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update"]
- apiGroups: ["cdi.kubevirt.io"]
resources:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: cdi-sa
labels:
cdi.kubevirt.io: ""
roleRef:
kind: ClusterRole
name: cdi
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: cdi-sa
namespace: {{ .Namespace }}
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: cdi-deployment
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: ""
app: containerized-data-importer
spec:
selector:
matchLabels:
app: containerized-data-importer
replicas: 1
template:
metadata:
labels:
app: containerized-data-importer
cdi.kubevirt.io: ""
spec:
serviceAccountName: cdi-sa
containers:
- name: cdi-controller
image: {{ .DockerRepo }}/{{ .ControllerImage }}:{{ .DockerTag }}
imagePullPolicy: {{ .PullPolicy }}
args: ["-v={{ .Verbosity }}"] # default verbosity; change to 2 or 3 for more detailed logging
env:
- name: IMPORTER_IMAGE
value: {{ .DockerRepo }}/{{ .ImporterImage }}:{{ .DockerTag }}
- name: CLONER_IMAGE
value: {{ .DockerRepo }}/{{ .ClonerImage }}:{{ .DockerTag }}
- name: UPLOADSERVER_IMAGE
value: {{ .DockerRepo }}/{{ .UploadServerImage }}:{{ .DockerTag }}
- name: PULL_POLICY
value: {{ .PullPolicy }}
- name: UPLOADPROXY_SERVICE
value: cdi-uploadproxy
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
labels:
cdi.kubevirt.io: ""
name: datavolumes.cdi.kubevirt.io
spec:
group: cdi.kubevirt.io
names:
kind: DataVolume
plural: datavolumes
shortNames:
- dv
- dvs
singular: datavolume
scope: Namespaced
validation:
openAPIV3Schema:
properties:
apiVersion:
type: string
kind:
type: string
metadata: {}
spec:
properties:
source:
properties:
http:
properties:
url:
type: string
secretRef:
type: string
required:
- url
s3:
properties:
url:
type: string
secretRef:
type: string
required:
- url
registry:
properties:
url:
type: string
secretRef:
type: string
required:
- url
pvc:
properties:
namespace:
type: string
name:
type: string
required:
- namespace
- name
upload: {}
blank: {}
pvc:
properties:
resources:
properties:
requests:
properties:
storage:
type: string
required:
- storage
required:
- requests
storageClassName:
type: string
accessModes:
type: array
required:
- resources
version: v1alpha1
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: cdi-apiserver
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: ""
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: cdi-apiserver
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: ""
roleRef:
kind: Role
name: cdi-apiserver
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: cdi-apiserver
namespace: {{ .Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: cdi-apiserver
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: ""
rules:
- apiGroups:
- ''
resources:
- secrets
verbs:
- get
- create
- apiGroups:
- ''
resources:
- configmaps
verbs:
- get
- create
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: cdi-extension-apiserver-authentication
namespace: kube-system
labels:
cdi.kubevirt.io: ""
roleRef:
kind: Role
name: cdi-extension-apiserver-authentication
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: cdi-apiserver
namespace: {{ .Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: cdi-extension-apiserver-authentication
namespace: kube-system
labels:
cdi.kubevirt.io: ""
rules:
- apiGroups:
- ''
resources:
- configmaps
resourceNames:
- extension-apiserver-authentication
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: cdi-apiserver
labels:
cdi.kubevirt.io: ""
roleRef:
kind: ClusterRole
name: cdi-apiserver
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: cdi-apiserver
namespace: {{ .Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: cdi-apiserver
labels:
cdi.kubevirt.io: ""
rules:
- apiGroups:
- admissionregistration.k8s.io
resources:
- validatingwebhookconfigurations
verbs:
- get
- create
- update
- apiGroups:
- apiregistration.k8s.io
resources:
- apiservices
verbs:
- get
- create
- update
- apiGroups:
- ''
resources:
- pods
- persistentvolumeclaims
verbs:
- get
- list
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: cdi-apiserver-auth-delegator
labels:
cdi.kubevirt.io: ""
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: cdi-apiserver
namespace: {{ .Namespace }}
---
apiVersion: v1
kind: Service
metadata:
name: cdi-api
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: "cdi-api"
spec:
ports:
- port: 443
targetPort: 8443
protocol: TCP
selector:
cdi.kubevirt.io: cdi-api
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: cdi-api
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: "cdi-api"
spec:
selector:
matchLabels:
cdi.kubevirt.io: "cdi-api"
replicas: 1
template:
metadata:
labels:
cdi.kubevirt.io: "cdi-api"
spec:
serviceAccountName: cdi-apiserver
containers:
- name: cdi-apiserver-api
image: {{ .DockerRepo }}/{{ .APIServerImage }}:{{ .DockerTag }}
imagePullPolicy: {{ .PullPolicy }}
args: ["-v={{ .Verbosity }}"] # default verbosity; change to 2 or 3 for more detailed logging
---
apiVersion: v1
kind: Service
metadata:
name: cdi-uploadproxy
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: "cdi-uploadproxy"
spec:
ports:
- port: 443
targetPort: 8443
protocol: TCP
selector:
cdi.kubevirt.io: cdi-uploadproxy
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: cdi-uploadproxy
namespace: {{ .Namespace }}
labels:
cdi.kubevirt.io: "cdi-uploadproxy"
spec:
selector:
matchLabels:
cdi.kubevirt.io: "cdi-uploadproxy"
replicas: 1
template:
metadata:
labels:
cdi.kubevirt.io: "cdi-uploadproxy"
spec:
containers:
- name: cdi-uploadproxy
image: {{ .DockerRepo }}/{{ .UploadProxyImage }}:{{ .DockerTag }}
imagePullPolicy: {{ .PullPolicy }}
args: ["-v={{ .Verbosity }}"] # default verbosity; change to 2 or 3 for more detailed logging
env:
- name: APISERVER_PUBLIC_KEY
valueFrom:
secretKeyRef:
name: cdi-api-signing-key
key: id_rsa.pub
- name: UPLOAD_SERVER_CLIENT_KEY
valueFrom:
secretKeyRef:
name: cdi-upload-server-client-key
key: tls.key
- name: UPLOAD_SERVER_CLIENT_CERT
valueFrom:
secretKeyRef:
name: cdi-upload-server-client-key
key: tls.crt
- name: UPLOAD_SERVER_CA_CERT
valueFrom:
secretKeyRef:
name: cdi-upload-server-client-key
key: ca.crt
- name: SERVICE_TLS_KEY
valueFrom:
secretKeyRef:
name: cdi-upload-proxy-server-key
key: tls.key
- name: SERVICE_TLS_CERT
valueFrom:
secretKeyRef:
name: cdi-upload-proxy-server-key
key: tls.crt
---
apiVersion: v1
kind: Service
metadata:
name: kubevirt-prometheus-metrics
namespace: {{.Namespace}}
labels:
prometheus.kubevirt.io: ""
kubevirt.io: ""
spec:
ports:
- name: metrics
port: 443
targetPort: metrics
protocol: TCP
selector:
prometheus.kubevirt.io: ""

View File

@ -0,0 +1,87 @@
/*
Copyright 2018 The CDI 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 cluster
import (
rbacv1 "k8s.io/api/rbac/v1"
)
const (
apiServerResourceName = "cdi-apiserver"
)
func createAPIServerResources(args *FactoryArgs) []Resource {
return []Resource{
createAPIServerClusterRoleBinding(args.Namespace),
createAPIServerClusterRole(),
createAPIServerAuthClusterRoleBinding(args.Namespace),
}
}
func createAPIServerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding {
return createClusterRoleBinding(apiServerResourceName, apiServerResourceName, apiServerResourceName, namespace)
}
func createAPIServerClusterRole() *rbacv1.ClusterRole {
clusterRole := createClusterRole(apiServerResourceName)
clusterRole.Rules = []rbacv1.PolicyRule{
{
APIGroups: []string{
"admissionregistration.k8s.io",
},
Resources: []string{
"validatingwebhookconfigurations",
},
Verbs: []string{
"get",
"create",
"update",
},
},
{
APIGroups: []string{
"apiregistration.k8s.io",
},
Resources: []string{
"apiservices",
},
Verbs: []string{
"get",
"create",
"update",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"pods",
"persistentvolumeclaims",
},
Verbs: []string{
"get",
"list",
},
},
}
return clusterRole
}
func createAPIServerAuthClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding {
return createClusterRoleBinding("cdi-apiserver-auth-delegator", "system:auth-delegator", apiServerResourceName, namespace)
}

View File

@ -0,0 +1,152 @@
/*
Copyright 2018 The CDI 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 cluster
import (
rbacv1 "k8s.io/api/rbac/v1"
)
const (
controllerServiceAccountName = "cdi-sa"
controlerClusterRoleName = "cdi"
)
func createControllerResources(args *FactoryArgs) []Resource {
return []Resource{
createControllerClusterRoleBinding(args.Namespace),
createControllerClusterRole(),
}
}
func createControllerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding {
return createClusterRoleBinding(controllerServiceAccountName, controlerClusterRoleName, controllerServiceAccountName, namespace)
}
func createControllerClusterRole() *rbacv1.ClusterRole {
clusterRole := createClusterRole(controlerClusterRoleName)
clusterRole.Rules = []rbacv1.PolicyRule{
{
APIGroups: []string{
"",
},
Resources: []string{
"events",
},
Verbs: []string{
"create",
"update",
"patch",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"persistentvolumeclaims",
},
Verbs: []string{
"get",
"list",
"watch",
"create",
"update",
"patch",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"persistentvolumeclaims/finalizers",
"pods/finalizers",
},
Verbs: []string{
"update",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"pods",
"services",
},
Verbs: []string{
"get",
"list",
"watch",
"create",
"delete",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"secrets",
},
Verbs: []string{
"get",
"list",
"watch",
"create",
},
},
{
APIGroups: []string{
"",
},
Resources: []string{
"configmaps",
},
Verbs: []string{
"get",
"list",
"watch",
"create",
"update",
},
},
{
APIGroups: []string{
"storage.k8s.io",
},
Resources: []string{
"storageclasses",
},
Verbs: []string{
"get",
},
},
{
APIGroups: []string{
"cdi.kubevirt.io",
},
Resources: []string{
"*",
},
Verbs: []string{
"*",
},
},
}
return clusterRole
}

View File

@ -0,0 +1,160 @@
/*
Copyright 2018 The CDI 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 cluster
import (
extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func createCRDResources(args *FactoryArgs) []Resource {
return []Resource{
createDataVolumeCRD(),
}
}
func createDataVolumeCRD() *extv1beta1.CustomResourceDefinition {
return &extv1beta1.CustomResourceDefinition{
TypeMeta: metav1.TypeMeta{
APIVersion: "apiextensions.k8s.io/v1beta1",
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "datavolumes.cdi.kubevirt.io",
Labels: map[string]string{
"cdi.kubevirt.io": "",
},
},
Spec: extv1beta1.CustomResourceDefinitionSpec{
Group: "cdi.kubevirt.io",
Names: extv1beta1.CustomResourceDefinitionNames{
Kind: "DataVolume",
Plural: "datavolumes",
ShortNames: []string{
"dv",
"dvs",
},
Singular: "datavolume",
},
Version: "v1alpha1",
Scope: "Namespaced",
Validation: &extv1beta1.CustomResourceValidation{
OpenAPIV3Schema: &extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"apiVersion": extv1beta1.JSONSchemaProps{
Type: "string",
},
"kind": extv1beta1.JSONSchemaProps{
Type: "string",
},
"metadata": extv1beta1.JSONSchemaProps{},
"spec": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"source": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"http": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"url": extv1beta1.JSONSchemaProps{
Type: "string",
},
"secretRef": extv1beta1.JSONSchemaProps{
Type: "string",
},
},
Required: []string{
"url",
},
},
"s3": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"url": extv1beta1.JSONSchemaProps{
Type: "string",
},
"secretRef": extv1beta1.JSONSchemaProps{
Type: "string",
},
},
Required: []string{
"url",
},
},
"registry": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"url": extv1beta1.JSONSchemaProps{
Type: "string",
},
"secretRef": extv1beta1.JSONSchemaProps{
Type: "string",
},
},
Required: []string{
"url",
},
},
"pvc": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"namespace": extv1beta1.JSONSchemaProps{
Type: "string",
},
"name": extv1beta1.JSONSchemaProps{
Type: "string",
},
},
Required: []string{
"namespace",
"name",
},
},
"upload": extv1beta1.JSONSchemaProps{},
"blank": extv1beta1.JSONSchemaProps{},
},
},
"pvc": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"resources": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"requests": extv1beta1.JSONSchemaProps{
Properties: map[string]extv1beta1.JSONSchemaProps{
"storage": extv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
},
Required: []string{
"requests",
},
},
"storageClassName": extv1beta1.JSONSchemaProps{
Type: "string",
},
"accessModes": extv1beta1.JSONSchemaProps{
Type: "array",
},
},
Required: []string{
"resources",
},
},
},
},
},
},
},
},
}
}

View File

@ -0,0 +1,61 @@
/*
Copyright 2018 The CDI 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 cluster
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
)
// FactoryArgs contains the required parameters to generate all cluster-scoped resources
type FactoryArgs struct {
Namespace string
}
// Resource defines the interface of all cluster-wide components
type Resource runtime.Object
type factoryFunc func(*FactoryArgs) []Resource
var factoryFunctions = map[string]factoryFunc{
"crd": createCRDResources,
"controller": createControllerResources,
"apiserver": createAPIServerResources,
}
// CreateAllResources creates all cluster-wide resources
func CreateAllResources(args *FactoryArgs) ([]Resource, error) {
resources := []Resource{}
for group := range factoryFunctions {
rs, err := CreateResourceGroup(group, args)
if err != nil {
return nil, err
}
resources = append(resources, rs...)
}
return resources, nil
}
// CreateResourceGroup creates all cluster resources fr a specific group/component
func CreateResourceGroup(group string, args *FactoryArgs) ([]Resource, error) {
f, ok := factoryFunctions[group]
if !ok {
return nil, fmt.Errorf("Group %s does not exist", group)
}
return f(args), nil
}

View File

@ -0,0 +1,64 @@
/*
Copyright 2018 The CDI 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 cluster
import (
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func createClusterRoleBinding(name, roleRef, serviceAccount, serviceAccountNamespace string) *rbacv1.ClusterRoleBinding {
return &rbacv1.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1",
Kind: "ClusterRoleBinding",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"cdi.kubevirt.io": "",
},
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: roleRef,
APIGroup: "rbac.authorization.k8s.io",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: serviceAccount,
Namespace: serviceAccountNamespace,
},
},
}
}
func createClusterRole(name string) *rbacv1.ClusterRole {
return &rbacv1.ClusterRole{
TypeMeta: metav1.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1",
Kind: "ClusterRole",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"cdi.kubevirt.io": "",
},
},
}
}

View File

@ -0,0 +1,127 @@
/*
Copyright 2018 The CDI 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 namespaced
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"kubevirt.io/containerized-data-importer/pkg/common"
)
const (
cdiLabel = common.CDIComponentLabel
apiServerRessouceName = "cdi-apiserver"
extensionAPIResourceName = "cdi-extension-apiserver-authentication"
extensionAPIConfigMapName = "extension-apiserver-authentication"
)
func createAPIServerResources(args *FactoryArgs) []Resource {
return []Resource{
createAPIServerServiceAccount(),
createAPIServerRoleBinding(args.Namespace),
createAPIServerRole(),
createExtensionAPIServerRoleBinding(args.Namespace),
createExtensionAPIServerRole(),
createAPIServerService(),
createAPIServerDeployment(args.DockerRepo, args.APIServerImage, args.DockerTag, args.Verbosity, args.PullPolicy),
}
}
func createAPIServerServiceAccount() *corev1.ServiceAccount {
return createServiceAccount(apiServerRessouceName)
}
func createAPIServerRoleBinding(serviceAccountNamespace string) *rbacv1.RoleBinding {
return createRoleBinding(apiServerRessouceName, apiServerRessouceName, apiServerRessouceName, serviceAccountNamespace)
}
func createAPIServerRole() *rbacv1.Role {
role := createRole(apiServerRessouceName)
role.Rules = []rbacv1.PolicyRule{
{
APIGroups: []string{
"",
},
Resources: []string{
"secrets",
"configmaps",
},
Verbs: []string{
"get",
"create",
},
},
}
return role
}
func createExtensionAPIServerRoleBinding(serviceAccountNamespace string) *rbacv1.RoleBinding {
roleBinding := createRoleBinding(
extensionAPIResourceName,
extensionAPIResourceName,
apiServerRessouceName,
serviceAccountNamespace,
)
roleBinding.Namespace = "kube-system"
return roleBinding
}
func createExtensionAPIServerRole() *rbacv1.Role {
role := createRole(extensionAPIResourceName)
role.Rules = []rbacv1.PolicyRule{
{
APIGroups: []string{
"",
},
Resources: []string{
"configmaps",
},
Verbs: []string{
"get",
},
ResourceNames: []string{
extensionAPIConfigMapName,
},
},
}
return role
}
func createAPIServerService() *corev1.Service {
service := createService("cdi-api", cdiLabel, apiServerRessouceName)
service.Spec.Ports = []corev1.ServicePort{
{
Port: 443,
TargetPort: intstr.IntOrString{
Type: intstr.Int,
IntVal: 8443,
},
Protocol: corev1.ProtocolTCP,
},
}
return service
}
func createAPIServerDeployment(repo, image, tag, verbosity, pullPolicy string) *appsv1.Deployment {
deployment := createDeployment(apiServerRessouceName, cdiLabel, apiServerRessouceName, apiServerRessouceName, 1)
container := createContainer(apiServerRessouceName, repo, image, tag, verbosity, corev1.PullPolicy(pullPolicy))
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
return deployment
}

View File

@ -0,0 +1,153 @@
/*
Copyright 2018 The CDI 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 namespaced
import (
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var commonLabels = map[string]string{
"cdi.kubevirt.io": "",
}
func withCommonLabels(labels map[string]string) map[string]string {
if labels == nil {
labels = make(map[string]string)
}
for k, v := range commonLabels {
_, ok := labels[k]
if !ok {
labels[k] = v
}
}
return labels
}
func createServiceAccount(name string) *corev1.ServiceAccount {
return &corev1.ServiceAccount{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "ServiceAccount",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: withCommonLabels(nil),
},
}
}
func createRoleBinding(name, roleRef, serviceAccount, serviceAccountNamespace string) *rbacv1.RoleBinding {
return &rbacv1.RoleBinding{
TypeMeta: metav1.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1",
Kind: "RoleBinding",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: withCommonLabels(nil),
},
RoleRef: rbacv1.RoleRef{
Kind: "Role",
Name: roleRef,
APIGroup: "rbac.authorization.k8s.io",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: serviceAccount,
Namespace: serviceAccountNamespace,
},
},
}
}
func createRole(name string) *rbacv1.Role {
return &rbacv1.Role{
TypeMeta: metav1.TypeMeta{
APIVersion: "rbac.authorization.k8s.io/v1",
Kind: "Role",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: withCommonLabels(nil),
},
}
}
func createDeployment(name, matchKey, matchValue, serviceAccount string, numReplicas int32) *appsv1.Deployment {
matchMap := map[string]string{matchKey: matchValue}
deployment := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: withCommonLabels(matchMap),
},
Spec: appsv1.DeploymentSpec{
Replicas: &numReplicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
matchKey: matchValue,
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: withCommonLabels(matchMap),
},
},
},
}
if serviceAccount != "" {
deployment.Spec.Template.Spec.ServiceAccountName = serviceAccount
}
return deployment
}
func createContainer(name, repo, image, tag, verbosity string, pullPolicy corev1.PullPolicy) corev1.Container {
return corev1.Container{
Name: name,
Image: fmt.Sprintf("%s/%s:%s", repo, image, tag),
ImagePullPolicy: pullPolicy,
Args: []string{"-v=" + verbosity},
}
}
func createService(name, matchKey, matchValue string) *corev1.Service {
matchMap := map[string]string{matchKey: matchValue}
return &corev1.Service{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Service",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: withCommonLabels(matchMap),
},
Spec: corev1.ServiceSpec{
Selector: matchMap,
},
}
}

View File

@ -0,0 +1,108 @@
/*
Copyright 2018 The CDI 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 namespaced
import (
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"kubevirt.io/containerized-data-importer/pkg/common"
)
const (
controllerServiceAccount = "cdi-sa"
prometheusLabel = common.PrometheusLabel
)
func createControllerResources(args *FactoryArgs) []Resource {
return []Resource{
createControllerServiceAccount(),
createControllerDeployment(args.DockerRepo,
args.ControllerImage,
args.ImporterImage,
args.ClonerImage,
args.UploadServerImage,
args.DockerTag,
args.Verbosity,
args.PullPolicy),
}
}
func createControllerServiceAccount() *corev1.ServiceAccount {
return createServiceAccount(controllerServiceAccount)
}
func createControllerDeployment(repo, controllerImage, importerImage, clonerImage, uploadServerImage, tag, verbosity, pullPolicy string) *appsv1.Deployment {
deployment := createDeployment("cdi-deployment", "app", "containerized-data-importer", controllerServiceAccount, int32(1))
container := createContainer("cdi-controller", repo, controllerImage, tag, verbosity, corev1.PullPolicy(pullPolicy))
container.Env = []corev1.EnvVar{
{
Name: "IMPORTER_IMAGE",
Value: fmt.Sprintf("%s/%s:%s", repo, importerImage, tag),
},
{
Name: "CLONER_IMAGE",
Value: fmt.Sprintf("%s/%s:%s", repo, clonerImage, tag),
},
{
Name: "UPLOADSERVER_IMAGE",
Value: fmt.Sprintf("%s/%s:%s", repo, uploadServerImage, tag),
},
{
Name: "UPLOADPROXY_SERVICE",
Value: uploadProxyResourceName,
},
}
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
return deployment
}
func createPrometheusService() *corev1.Service {
return &corev1.Service{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "Service",
},
ObjectMeta: metav1.ObjectMeta{
Name: "kubevirt-prometheus-metrics",
Labels: map[string]string{
prometheusLabel: "",
"kubevirt.io": "",
},
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
prometheusLabel: "",
},
Ports: []corev1.ServicePort{
{
Name: "metrics",
Port: 443,
TargetPort: intstr.IntOrString{
Type: intstr.String,
StrVal: "metrics",
},
Protocol: corev1.ProtocolTCP,
},
},
},
}
}

View File

@ -0,0 +1,66 @@
package namespaced
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
)
// FactoryArgs contains the required parameters to generate all namespaced resources
type FactoryArgs struct {
DockerRepo string
DockerTag string
ControllerImage string
ImporterImage string
ClonerImage string
APIServerImage string
UploadProxyImage string
UploadServerImage string
Verbosity string
PullPolicy string
Namespace string
}
type factoryFunc func(*FactoryArgs) []Resource
// Resource defines the interface for namespaced resources
type Resource interface {
runtime.Object
SetNamespace(string)
GetNamespace() string
}
var factoryFunctions = map[string]factoryFunc{
"apiserver": createAPIServerResources,
"controller": createControllerResources,
"uploadproxy": createUploadProxyResources,
}
// CreateAllResources creates all namespaced resources
func CreateAllResources(args *FactoryArgs) ([]Resource, error) {
resources := []Resource{}
for group := range factoryFunctions {
rs, err := CreateResourceGroup(group, args)
if err != nil {
return nil, err
}
resources = append(resources, rs...)
}
return resources, nil
}
// CreateResourceGroup creates namespaced resources for a specific group/component
func CreateResourceGroup(group string, args *FactoryArgs) ([]Resource, error) {
f, ok := factoryFunctions[group]
if !ok {
return nil, fmt.Errorf("Group %s does not exist", group)
}
resources := []Resource{}
for _, o := range f(args) {
if o.GetNamespace() == "" {
o.SetNamespace(args.Namespace)
}
resources = append(resources, o)
}
return resources, nil
}

View File

@ -0,0 +1,124 @@
/*
Copyright 2018 The CDI 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 namespaced
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
const (
uploadProxyResourceName = "cdi-uploadproxy"
)
func createUploadProxyResources(args *FactoryArgs) []Resource {
return []Resource{
createUploadProxyService(),
createUploadProxyDeployment(args.DockerRepo, args.UploadProxyImage, args.DockerTag, args.Verbosity, args.PullPolicy),
}
}
func createUploadProxyService() *corev1.Service {
service := createService(uploadProxyResourceName, "cdi.kubevirt.io", uploadProxyResourceName)
service.Spec.Ports = []corev1.ServicePort{
{
Port: 443,
TargetPort: intstr.IntOrString{
Type: intstr.Int,
IntVal: 8443,
},
Protocol: corev1.ProtocolTCP,
},
}
return service
}
func createUploadProxyDeployment(repo, image, tag, verbosity, pullPolicy string) *appsv1.Deployment {
deployment := createDeployment(uploadProxyResourceName, "cdi.kubevirt.io", uploadProxyResourceName, "", int32(1))
container := createContainer(uploadProxyResourceName, repo, image, tag, verbosity, corev1.PullPolicy(pullPolicy))
container.Env = []corev1.EnvVar{
{
Name: "APISERVER_PUBLIC_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cdi-api-signing-key",
},
Key: "id_rsa.pub",
},
},
},
{
Name: "UPLOAD_SERVER_CLIENT_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cdi-upload-server-client-key",
},
Key: "tls.key",
},
},
},
{
Name: "UPLOAD_SERVER_CLIENT_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cdi-upload-server-client-key",
},
Key: "tls.crt",
},
},
},
{
Name: "UPLOAD_SERVER_CA_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cdi-upload-server-client-key",
},
Key: "ca.crt",
},
},
},
{
Name: "SERVICE_TLS_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cdi-upload-proxy-server-key",
},
Key: "tls.key",
},
},
},
{
Name: "SERVICE_TLS_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "cdi-upload-proxy-server-key",
},
Key: "tls.crt",
},
},
},
}
deployment.Spec.Template.Spec.Containers = []corev1.Container{container}
return deployment
}

View File

@ -18,9 +18,12 @@ import (
"text/template"
"github.com/golang/glog"
cdicluster "kubevirt.io/containerized-data-importer/pkg/operator/resources/cluster"
cdinamespaced "kubevirt.io/containerized-data-importer/pkg/operator/resources/namespaced"
)
type data struct {
type templateData struct {
DockerRepo string
DockerTag string
ControllerImage string
@ -35,23 +38,36 @@ type data struct {
Namespace string
}
var (
dockerRepo = flag.String("docker-repo", "", "")
dockertag = flag.String("docker-tag", "", "")
controllerImage = flag.String("controller-image", "", "")
importerImage = flag.String("importer-image", "", "")
clonerImage = flag.String("cloner-image", "", "")
apiServerImage = flag.String("apiserver-image", "", "")
uploadProxyImage = flag.String("uploadproxy-image", "", "")
uploadServerImage = flag.String("uploadserver-image", "", "")
operatorImage = flag.String("operator-image", "", "")
verbosity = flag.String("verbosity", "1", "")
pullPolicy = flag.String("pull-policy", "", "")
namespace = flag.String("namespace", "", "")
)
func main() {
dockerRepo := flag.String("docker-repo", "", "")
dockertag := flag.String("docker-tag", "", "")
controllerImage := flag.String("controller-image", "", "")
importerImage := flag.String("importer-image", "", "")
clonerImage := flag.String("cloner-image", "", "")
apiServerImage := flag.String("apiserver-image", "", "")
uploadProxyImage := flag.String("uploadproxy-image", "", "")
uploadServerImage := flag.String("uploadserver-image", "", "")
operatorImage := flag.String("operator-image", "", "")
templFile := flag.String("template", "", "")
verbosity := flag.String("verbosity", "1", "")
pullPolicy := flag.String("pull-policy", "", "")
namespace := flag.String("namespace", "", "")
codeGroup := flag.String("code-group", "everything", "")
flag.Parse()
data := &data{
if *templFile != "" {
generateFromFile(*templFile)
return
}
generateFromCode(*codeGroup)
}
func generateFromFile(templFile string) {
data := &templateData{
Verbosity: *verbosity,
DockerRepo: *dockerRepo,
DockerTag: *dockertag,
@ -66,15 +82,108 @@ func main() {
Namespace: *namespace,
}
file, err := os.OpenFile(*templFile, os.O_RDONLY, 0)
file, err := os.OpenFile(templFile, os.O_RDONLY, 0)
if err != nil {
glog.Fatalf("Failed to open file %s: %v\n", *templFile, err)
glog.Fatalf("Failed to open file %s: %v\n", templFile, err)
}
defer file.Close()
tmpl := template.Must(template.ParseFiles(*templFile))
tmpl := template.Must(template.ParseFiles(templFile))
err = tmpl.Execute(os.Stdout, data)
if err != nil {
glog.Fatalf("Error executing template: %v\n", err)
}
}
func generateFromCode(codeGroup string) {
var resources []interface{}
crs, err := getClusterResources(codeGroup)
if err != nil {
glog.Fatalf("Error getting cluster resources: %v\n", err)
}
resources = append(resources, crs...)
nsrs, err := getNamespacedResources(codeGroup)
if err != nil {
glog.Fatalf("Error getting namespaced resources: %v\n", err)
}
resources = append(resources, nsrs...)
for _, resource := range resources {
err = MarshallObject(resource, os.Stdout)
if err != nil {
glog.Fatalf("Error marshalling resource: %v\n", err)
}
}
}
// getClusterResources creates cluster-scoped resources for a specific group/component
// returning interface{} because caller is only interested in marshalling to json
// and is stuffing cluster-scomed and namespaced resources in the same slice
func getClusterResources(codeGroup string) ([]interface{}, error) {
var result []interface{}
var resources []cdicluster.Resource
var err error
args := &cdicluster.FactoryArgs{
Namespace: *namespace,
}
if codeGroup == "everything" {
resources, err = cdicluster.CreateAllResources(args)
} else {
resources, err = cdicluster.CreateResourceGroup(codeGroup, args)
}
if err != nil {
return nil, err
}
for _, resource := range resources {
result = append(result, resource.(interface{}))
}
return result, nil
}
// getNamespacedResources creates namespace-scoped resources for a specific group/component
// returning interface{} because caller is only interested in marshalling to json
// and is stuffing cluster-scomed and namespaced resources in the same slice
func getNamespacedResources(codeGroup string) ([]interface{}, error) {
var result []interface{}
var resources []cdinamespaced.Resource
var err error
args := &cdinamespaced.FactoryArgs{
Verbosity: *verbosity,
DockerRepo: *dockerRepo,
DockerTag: *dockertag,
ControllerImage: *controllerImage,
ImporterImage: *importerImage,
ClonerImage: *clonerImage,
APIServerImage: *apiServerImage,
UploadProxyImage: *uploadProxyImage,
UploadServerImage: *uploadServerImage,
PullPolicy: *pullPolicy,
Namespace: *namespace,
}
if codeGroup == "everything" {
resources, err = cdinamespaced.CreateAllResources(args)
} else {
resources, err = cdinamespaced.CreateResourceGroup(codeGroup, args)
}
if err != nil {
return nil, err
}
for _, resource := range resources {
result = append(result, resource.(interface{}))
}
return result, nil
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of the KubeVirt project
*
* 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.
*
* Copyright 2018 Red Hat, Inc.
*
*/
// this file directly copy/pasted from KubeVirt project here:
// https://github.com/kubevirt/kubevirt/blob/master/tools/util/marshaller.go
package main
import (
"encoding/json"
"io"
"strings"
"github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// MarshallObject marshalls an object to yaml appropriate for kubectl
func MarshallObject(obj interface{}, writer io.Writer) error {
jsonBytes, err := json.Marshal(obj)
if err != nil {
return err
}
var r unstructured.Unstructured
if err := json.Unmarshal(jsonBytes, &r.Object); err != nil {
return err
}
// remove status and metadata.creationTimestamp
unstructured.RemoveNestedField(r.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(r.Object, "spec", "template", "metadata", "creationTimestamp")
unstructured.RemoveNestedField(r.Object, "status")
jsonBytes, err = json.Marshal(r.Object)
if err != nil {
return err
}
yamlBytes, err := yaml.JSONToYAML(jsonBytes)
if err != nil {
return err
}
// fix templates by removing quotes...
s := string(yamlBytes)
s = strings.Replace(s, "'{{", "{{", -1)
s = strings.Replace(s, "}}'", "}}", -1)
yamlBytes = []byte(s)
_, err = writer.Write([]byte("---\n"))
if err != nil {
return err
}
_, err = writer.Write(yamlBytes)
if err != nil {
return err
}
return nil
}

View File

@ -360,7 +360,32 @@ func TestFormatGeneric(t *testing.T) {
}
}
func wrappedNew(message string) error { // This function will be mid-stack inlined in go 1.12+
return New(message)
}
func TestFormatWrappedNew(t *testing.T) {
tests := []struct {
error
format string
want string
}{{
wrappedNew("error"),
"%+v",
"error\n" +
"github.com/pkg/errors.wrappedNew\n" +
"\t.+/github.com/pkg/errors/format_test.go:364\n" +
"github.com/pkg/errors.TestFormatWrappedNew\n" +
"\t.+/github.com/pkg/errors/format_test.go:373",
}}
for i, tt := range tests {
testFormatRegexp(t, i, tt.error, tt.format, tt.want)
}
}
func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
t.Helper()
got := fmt.Sprintf(format, arg)
gotLines := strings.SplitN(got, "\n", -1)
wantLines := strings.SplitN(want, "\n", -1)

137
vendor/github.com/pkg/errors/stack.go generated vendored
View File

@ -1,7 +1,6 @@
package errors
import (
"bytes"
"fmt"
"io"
"path"
@ -11,7 +10,44 @@ import (
)
// Frame represents a program counter inside a stack frame.
type Frame runtime.Frame
// For historical reasons if Frame is interpreted as a uintptr
// its value represents the program counter + 1.
type Frame uintptr
// pc returns the program counter for this frame;
// multiple frames may have the same PC value.
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
// file returns the full path to the file that contains the
// function for this Frame's pc.
func (f Frame) file() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
file, _ := fn.FileLine(f.pc())
return file
}
// line returns the line number of source code of the
// function for this Frame's pc.
func (f Frame) line() int {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return 0
}
_, line := fn.FileLine(f.pc())
return line
}
// name returns the name of this function, if known.
func (f Frame) name() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
return fn.Name()
}
// Format formats the frame according to the fmt.Formatter interface.
//
@ -26,39 +62,24 @@ type Frame runtime.Frame
// GOPATH separated by \n\t (<funcname>\n\t<path>)
// %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) {
f.format(s, s, verb)
}
// format allows stack trace printing calls to be made with a bytes.Buffer.
func (f Frame) format(w io.Writer, s fmt.State, verb rune) {
switch verb {
case 's':
switch {
case s.Flag('+'):
fn := f.Func
if fn == nil {
io.WriteString(w, "unknown")
} else {
io.WriteString(w, fn.Name())
io.WriteString(w, "\n\t")
io.WriteString(w, f.File)
}
io.WriteString(s, f.name())
io.WriteString(s, "\n\t")
io.WriteString(s, f.file())
default:
file := f.File
if file == "" {
file = "unknown"
}
io.WriteString(w, path.Base(file))
io.WriteString(s, path.Base(f.file()))
}
case 'd':
io.WriteString(w, strconv.Itoa(f.Line))
io.WriteString(s, strconv.Itoa(f.line()))
case 'n':
name := f.Function
io.WriteString(s, funcname(name))
io.WriteString(s, funcname(f.name()))
case 'v':
f.format(w, s, 's')
io.WriteString(w, ":")
f.format(w, s, 'd')
f.Format(s, 's')
io.WriteString(s, ":")
f.Format(s, 'd')
}
}
@ -74,50 +95,37 @@ type StackTrace []Frame
//
// %+v Prints filename, function, and line number for each Frame in the stack.
func (st StackTrace) Format(s fmt.State, verb rune) {
var b bytes.Buffer
switch verb {
case 'v':
switch {
case s.Flag('+'):
b.Grow(len(st) * stackMinLen)
for _, fr := range st {
b.WriteByte('\n')
fr.format(&b, s, verb)
for _, f := range st {
io.WriteString(s, "\n")
f.Format(s, verb)
}
case s.Flag('#'):
fmt.Fprintf(&b, "%#v", []Frame(st))
fmt.Fprintf(s, "%#v", []Frame(st))
default:
st.formatSlice(&b, s, verb)
st.formatSlice(s, verb)
}
case 's':
st.formatSlice(&b, s, verb)
st.formatSlice(s, verb)
}
io.Copy(s, &b)
}
// formatSlice will format this StackTrace into the given buffer as a slice of
// Frame, only valid when called with '%s' or '%v'.
func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) {
b.WriteByte('[')
if len(st) == 0 {
b.WriteByte(']')
return
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
io.WriteString(s, "[")
for i, f := range st {
if i > 0 {
io.WriteString(s, " ")
}
f.Format(s, verb)
}
b.Grow(len(st) * (stackMinLen / 4))
st[0].format(b, s, verb)
for _, fr := range st[1:] {
b.WriteByte(' ')
fr.format(b, s, verb)
}
b.WriteByte(']')
io.WriteString(s, "]")
}
// stackMinLen is a best-guess at the minimum length of a stack trace. It
// doesn't need to be exact, just give a good enough head start for the buffer
// to avoid the expensive early growth.
const stackMinLen = 96
// stack represents a stack of program counters.
type stack []uintptr
@ -126,29 +134,20 @@ func (s *stack) Format(st fmt.State, verb rune) {
case 'v':
switch {
case st.Flag('+'):
frames := runtime.CallersFrames(*s)
for {
frame, more := frames.Next()
fmt.Fprintf(st, "\n%+v", Frame(frame))
if !more {
break
}
for _, pc := range *s {
f := Frame(pc)
fmt.Fprintf(st, "\n%+v", f)
}
}
}
}
func (s *stack) StackTrace() StackTrace {
var st []Frame
frames := runtime.CallersFrames(*s)
for {
frame, more := frames.Next()
st = append(st, Frame(frame))
if !more {
break
}
f := make([]Frame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = Frame((*s)[i])
}
return st
return f
}
func callers() *stack {

View File

@ -35,11 +35,11 @@ func TestFrameFormat(t *testing.T) {
"github.com/pkg/errors.init\n" +
"\t.+/github.com/pkg/errors/stack_test.go",
}, {
Frame{},
0,
"%s",
"unknown",
}, {
Frame{},
0,
"%+s",
"unknown",
}, {
@ -47,7 +47,7 @@ func TestFrameFormat(t *testing.T) {
"%d",
"9",
}, {
Frame{},
0,
"%d",
"0",
}, {
@ -69,7 +69,7 @@ func TestFrameFormat(t *testing.T) {
"%n",
"X.val",
}, {
Frame{},
0,
"%n",
"",
}, {
@ -82,7 +82,7 @@ func TestFrameFormat(t *testing.T) {
"github.com/pkg/errors.init\n" +
"\t.+/github.com/pkg/errors/stack_test.go:9",
}, {
Frame{},
0,
"%v",
"unknown:0",
}}
@ -133,7 +133,7 @@ func TestStackTrace(t *testing.T) {
"\t.+/github.com/pkg/errors/stack_test.go:131", // this is the stack of New
},
}, {
func() error { noinline(); return New("ooh") }(), []string{
func() error { return New("ooh") }(), []string{
`github.com/pkg/errors.TestStackTrace.func1` +
"\n\t.+/github.com/pkg/errors/stack_test.go:136", // this is the stack of New
"github.com/pkg/errors.TestStackTrace\n" +
@ -246,9 +246,5 @@ func caller() Frame {
n := runtime.Callers(2, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
frame, _ := frames.Next()
return Frame(frame)
return Frame(frame.PC)
}
//go:noinline
// noinline prevents the caller being inlined
func noinline() {}

View File

@ -0,0 +1,2 @@
Sorry, we do not accept changes directly against this repository. Please see
CONTRIBUTING.md for information on where and how to contribute instead.

View File

@ -0,0 +1,7 @@
# Contributing guidelines
Do not open pull requests directly against this repository, they will be ignored. Instead, please open pull requests against [kubernetes/kubernetes](https://git.k8s.io/kubernetes/). Please follow the same [contributing guide](https://git.k8s.io/kubernetes/CONTRIBUTING.md) you would follow for any other pull request made to kubernetes/kubernetes.
This repository is published from [kubernetes/kubernetes/staging/src/k8s.io/apiextensions-apiserver](https://git.k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver) by the [kubernetes publishing-bot](https://git.k8s.io/publishing-bot).
Please see [Staging Directory and Publishing](https://git.k8s.io/community/contributors/devel/staging.md) for more information

2186
vendor/k8s.io/apiextensions-apiserver/Godeps/Godeps.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

2
vendor/k8s.io/apiextensions-apiserver/Godeps/OWNERS generated vendored Normal file
View File

@ -0,0 +1,2 @@
approvers:
- dep-approvers

5
vendor/k8s.io/apiextensions-apiserver/Godeps/Readme generated vendored Normal file
View File

@ -0,0 +1,5 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

202
vendor/k8s.io/apiextensions-apiserver/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

11
vendor/k8s.io/apiextensions-apiserver/OWNERS generated vendored Normal file
View File

@ -0,0 +1,11 @@
reviewers:
- deads2k
- sttts
- enisoc
- mbohlool
approvers:
- deads2k
- lavalamp
- sttts
labels:
- sig/api-machinery

20
vendor/k8s.io/apiextensions-apiserver/README.md generated vendored Normal file
View File

@ -0,0 +1,20 @@
# apiextensions-apiserver
Implements: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/thirdpartyresources.md
It provides an API for registering `CustomResourceDefinitions`.
## Purpose
This API server provides the implementation for `CustomResourceDefinitions` which is included as
delegate server inside of `kube-apiserver`.
## Compatibility
HEAD of this repo will match HEAD of k8s.io/apiserver, k8s.io/apimachinery, and k8s.io/client-go.
## Where does it come from?
`apiextensions-apiserver` is synced from https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiextensions-apiserver.
Code changes are made in that location, merged into `k8s.io/kubernetes` and later synced here.

View File

@ -0,0 +1,17 @@
# Defined below are the security contacts for this repo.
#
# They are the contact point for the Product Security Team to reach out
# to for triaging and handling of incoming issues.
#
# The below names agree to abide by the
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
# and will be removed and replaced if they violate that agreement.
#
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://kubernetes.io/security/
cjcullen
jessfraz
liggitt
philips
tallclair

View File

@ -0,0 +1,12 @@
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
name: v1alpha1.mygroup.example.com
spec:
insecureSkipTLSVerify: true
group: mygroup.example.com
priority: 500
service:
name: api
namespace: apiextensions
version: v1alpha1

View File

@ -0,0 +1,13 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: noxus.mygroup.example.com
spec:
group: mygroup.example.com
version: v1alpha1
scope: Namespaced
names:
plural: noxus
singular: noxu
kind: Noxu
listKind: NoxuList

View File

@ -0,0 +1,6 @@
apiVersion: mygroup.example.com/v1alpha1
kind: Noxu
metadata:
name: alfa-noxu
spec:
key: value

View File

@ -0,0 +1,12 @@
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
name: v1alpha1.apiextensions.k8s.io
spec:
insecureSkipTLSVerify: true
group: apiextensions.k8s.io
priority: 100
service:
name: api
namespace: apiextensions
version: v1alpha1

View File

@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: apiextensions:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: apiserver
namespace: apiextensions

View File

@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: apiextensions-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: apiserver
namespace: apiextensions

View File

@ -0,0 +1,26 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: apiextensions-server
namespace: apiextensions
labels:
apiserver: "true"
spec:
replicas: 1
selector:
apiserver: "true"
template:
metadata:
labels:
apiserver: "true"
spec:
serviceAccountName: apiserver
containers:
- name: apiextensions-server
image: apiextensions-apiserver:latest
imagePullPolicy: Never
args:
- "--etcd-servers=http://localhost:2379"
- "--audit-log-path=-"
- name: etcd
image: quay.io/coreos/etcd:v3.2.24

View File

@ -0,0 +1,5 @@
kind: ServiceAccount
apiVersion: v1
metadata:
name: apiserver
namespace: apiextensions

View File

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: api
namespace: apiextensions
spec:
ports:
- port: 443
protocol: TCP
targetPort: 443
selector:
apiserver: "true"

View File

@ -0,0 +1,17 @@
# 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.
FROM fedora
ADD apiextensions-apiserver /
ENTRYPOINT ["/apiextensions-apiserver"]

View File

@ -0,0 +1,3 @@
# Kubernetes Community Code of Conduct
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)

View File

@ -0,0 +1,57 @@
# Custom Resource Example
**Note:** CustomResourceDefinition is the successor of the deprecated ThirdPartyResource.
This particular example demonstrates how to generate a client for CustomResources using [`k8s.io/code-generator`](https://github.com/kubernetes/code-generator). The clientset can
be generated using the `./hack/update-codegen.sh` script.
The `update-codegen` script will automatically generate the following files and
directories:
* `pkg/apis/cr/v1/zz_generated.deepcopy.go`
* `pkg/client/`
The following code-generators are used:
* `deepcopy-gen` - creates a method `func (t* T) DeepCopy() *T` for each type T
* `client-gen` - creates typed clientsets for CustomResource APIGroups
* `informer-gen` - creates informers for CustomResources which offer an event based
interface to react on changes of CustomResources on the server
* `lister-gen` - creates listers for CustomResources which offer a read-only caching layer for GET and LIST requests.
Changes should not be made to these files manually, and when creating your own
controller based off of this implementation you should not copy these files and
instead run the `update-codegen` script to generate your own.
Please see [`k8s.io/sample-controller`](https://github.com/kubernetes/sample-controller) for an example
controller for CustomResources using the generated client.
## Use Cases
CustomResourceDefinitions can be used to implement custom resource types for your Kubernetes cluster.
These act like most other Resources in Kubernetes, and may be `kubectl apply`'d, etc.
Some example use cases:
* Provisioning/Management of external datastores/databases (eg. CloudSQL/RDS instances)
* Higher level abstractions around Kubernetes primitives (eg. a single Resource to define an etcd cluster, backed by a Service and a ReplicationController)
## Defining types
Each instance of your custom resource has an attached Spec, which should be defined via a `struct{}` to provide data format validation.
In practice, this Spec is arbitrary key-value data that specifies the configuration/behavior of your Resource.
For example, if you were implementing a custom resource for a Database, you might provide a DatabaseSpec like the following:
``` go
type DatabaseSpec struct {
Databases []string `json:"databases"`
Users []User `json:"users"`
Version string `json:"version"`
}
type User struct {
Name string `json:"name"`
Password string `json:"password"`
}
```

View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
# 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.
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
# generate the code with:
# --output-base because this script should also be able to run inside the vendor dir of
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
# instead of the $GOPATH directly. For normal projects this can be dropped.
${CODEGEN_PKG}/generate-groups.sh all \
k8s.io/apiextensions-apiserver/examples/client-go/pkg/client k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis \
cr:v1 \
--output-base "$(dirname ${BASH_SOURCE})/../../../../.."
# To use your own boilerplate text append:
# --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt

View File

@ -0,0 +1,48 @@
#!/usr/bin/env bash
# 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.
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
DIFFROOT="${SCRIPT_ROOT}/pkg"
TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg"
_tmp="${SCRIPT_ROOT}/_tmp"
cleanup() {
rm -rf "${_tmp}"
}
trap "cleanup" EXIT SIGINT
cleanup
mkdir -p "${TMP_DIFFROOT}"
cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}"
"${SCRIPT_ROOT}/hack/update-codegen.sh"
echo "diffing ${DIFFROOT} against freshly generated codegen"
ret=0
diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$?
cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}"
if [[ $ret -eq 0 ]]
then
echo "${DIFFROOT} up to date."
else
echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh"
exit 1
fi

View File

@ -0,0 +1,21 @@
/*
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 cr
const (
GroupName = "cr.example.apiextensions.k8s.io"
)

View File

@ -0,0 +1,21 @@
/*
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
// Package v1 is the v1 version of the API.
// +groupName=cr.example.apiextensions.k8s.io
package v1

View File

@ -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 v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
cr "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: cr.GroupName, Version: "v1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Example{},
&ExampleList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,63 @@
/*
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 v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Example is a specification for an Example resource
type Example struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec ExampleSpec `json:"spec"`
Status ExampleStatus `json:"status,omitempty"`
}
// ExampleSpec is the spec for an Example resource
type ExampleSpec struct {
Foo string `json:"foo"`
Bar bool `json:"bar"`
}
// ExampleStatus is the status for an Example resource
type ExampleStatus struct {
State ExampleState `json:"state,omitempty"`
Message string `json:"message,omitempty"`
}
type ExampleState string
const (
ExampleStateCreated ExampleState = "Created"
ExampleStateProcessed ExampleState = "Processed"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ExampleList is a list of Example resources
type ExampleList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Example `json:"items"`
}

View File

@ -0,0 +1,118 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Example) DeepCopyInto(out *Example) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Example.
func (in *Example) DeepCopy() *Example {
if in == nil {
return nil
}
out := new(Example)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Example) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExampleList) DeepCopyInto(out *ExampleList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Example, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleList.
func (in *ExampleList) DeepCopy() *ExampleList {
if in == nil {
return nil
}
out := new(ExampleList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ExampleList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExampleSpec) DeepCopyInto(out *ExampleSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleSpec.
func (in *ExampleSpec) DeepCopy() *ExampleSpec {
if in == nil {
return nil
}
out := new(ExampleSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExampleStatus) DeepCopyInto(out *ExampleStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExampleStatus.
func (in *ExampleStatus) DeepCopy() *ExampleStatus {
if in == nil {
return nil
}
out := new(ExampleStatus)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,98 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package versioned
import (
crv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned/typed/cr/v1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
flowcontrol "k8s.io/client-go/util/flowcontrol"
)
type Interface interface {
Discovery() discovery.DiscoveryInterface
CrV1() crv1.CrV1Interface
// Deprecated: please explicitly pick a version if possible.
Cr() crv1.CrV1Interface
}
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
crV1 *crv1.CrV1Client
}
// CrV1 retrieves the CrV1Client
func (c *Clientset) CrV1() crv1.CrV1Interface {
return c.crV1
}
// Deprecated: Cr retrieves the default version of CrClient.
// Please explicitly pick a version.
func (c *Clientset) Cr() crv1.CrV1Interface {
return c.crV1
}
// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
if c == nil {
return nil
}
return c.DiscoveryClient
}
// NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
}
var cs Clientset
var err error
cs.crV1, err = crv1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
return &cs, nil
}
// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.crV1 = crv1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
return &cs
}
// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.crV1 = crv1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs
}

View File

@ -0,0 +1,20 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated clientset.
package versioned

View File

@ -0,0 +1,82 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
clientset "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned"
crv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned/typed/cr/v1"
fakecrv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned/typed/cr/v1/fake"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/testing"
)
// NewSimpleClientset returns a clientset that will respond with the provided objects.
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
// without applying any validations and/or defaults. It shouldn't be considered a replacement
// for a real clientset and is mostly useful in simple unit tests.
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {
if err := o.Add(obj); err != nil {
panic(err)
}
}
cs := &Clientset{}
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
gvr := action.GetResource()
ns := action.GetNamespace()
watch, err := o.Watch(gvr, ns)
if err != nil {
return false, nil, err
}
return true, watch, nil
})
return cs
}
// Clientset implements clientset.Interface. Meant to be embedded into a
// struct to get a default implementation. This makes faking out just the method
// you want to test easier.
type Clientset struct {
testing.Fake
discovery *fakediscovery.FakeDiscovery
}
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
return c.discovery
}
var _ clientset.Interface = &Clientset{}
// CrV1 retrieves the CrV1Client
func (c *Clientset) CrV1() crv1.CrV1Interface {
return &fakecrv1.FakeCrV1{Fake: &c.Fake}
}
// Cr retrieves the CrV1Client
func (c *Clientset) Cr() crv1.CrV1Interface {
return &fakecrv1.FakeCrV1{Fake: &c.Fake}
}

View File

@ -0,0 +1,20 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated fake clientset.
package fake

View File

@ -0,0 +1,56 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
crv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
crv1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme
func init() {
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(AddToScheme(scheme))
}

View File

@ -0,0 +1,20 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package contains the scheme of the automatically generated clientset.
package scheme

View File

@ -0,0 +1,56 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package scheme
import (
crv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
crv1.AddToScheme,
}
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme
func init() {
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(AddToScheme(Scheme))
}

View File

@ -0,0 +1,90 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
"k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned/scheme"
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
rest "k8s.io/client-go/rest"
)
type CrV1Interface interface {
RESTClient() rest.Interface
ExamplesGetter
}
// CrV1Client is used to interact with features provided by the cr.example.apiextensions.k8s.io group.
type CrV1Client struct {
restClient rest.Interface
}
func (c *CrV1Client) Examples(namespace string) ExampleInterface {
return newExamples(c, namespace)
}
// NewForConfig creates a new CrV1Client for the given config.
func NewForConfig(c *rest.Config) (*CrV1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &CrV1Client{client}, nil
}
// NewForConfigOrDie creates a new CrV1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *CrV1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new CrV1Client for the given RESTClient.
func New(c rest.Interface) *CrV1Client {
return &CrV1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *CrV1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@ -0,0 +1,20 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1

View File

@ -0,0 +1,157 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
scheme "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// ExamplesGetter has a method to return a ExampleInterface.
// A group's client should implement this interface.
type ExamplesGetter interface {
Examples(namespace string) ExampleInterface
}
// ExampleInterface has methods to work with Example resources.
type ExampleInterface interface {
Create(*v1.Example) (*v1.Example, error)
Update(*v1.Example) (*v1.Example, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(name string, options metav1.GetOptions) (*v1.Example, error)
List(opts metav1.ListOptions) (*v1.ExampleList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Example, err error)
ExampleExpansion
}
// examples implements ExampleInterface
type examples struct {
client rest.Interface
ns string
}
// newExamples returns a Examples
func newExamples(c *CrV1Client, namespace string) *examples {
return &examples{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the example, and returns the corresponding example object, and an error if there is any.
func (c *examples) Get(name string, options metav1.GetOptions) (result *v1.Example, err error) {
result = &v1.Example{}
err = c.client.Get().
Namespace(c.ns).
Resource("examples").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of Examples that match those selectors.
func (c *examples) List(opts metav1.ListOptions) (result *v1.ExampleList, err error) {
result = &v1.ExampleList{}
err = c.client.Get().
Namespace(c.ns).
Resource("examples").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested examples.
func (c *examples) Watch(opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("examples").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a example and creates it. Returns the server's representation of the example, and an error, if there is any.
func (c *examples) Create(example *v1.Example) (result *v1.Example, err error) {
result = &v1.Example{}
err = c.client.Post().
Namespace(c.ns).
Resource("examples").
Body(example).
Do().
Into(result)
return
}
// Update takes the representation of a example and updates it. Returns the server's representation of the example, and an error, if there is any.
func (c *examples) Update(example *v1.Example) (result *v1.Example, err error) {
result = &v1.Example{}
err = c.client.Put().
Namespace(c.ns).
Resource("examples").
Name(example.Name).
Body(example).
Do().
Into(result)
return
}
// Delete takes name of the example and deletes it. Returns an error if one occurs.
func (c *examples) Delete(name string, options *metav1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("examples").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *examples) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("examples").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched example.
func (c *examples) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Example, err error) {
result = &v1.Example{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("examples").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -0,0 +1,20 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View File

@ -0,0 +1,40 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned/typed/cr/v1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeCrV1 struct {
*testing.Fake
}
func (c *FakeCrV1) Examples(namespace string) v1.ExampleInterface {
return &FakeExamples{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeCrV1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@ -0,0 +1,128 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
crv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeExamples implements ExampleInterface
type FakeExamples struct {
Fake *FakeCrV1
ns string
}
var examplesResource = schema.GroupVersionResource{Group: "cr.example.apiextensions.k8s.io", Version: "v1", Resource: "examples"}
var examplesKind = schema.GroupVersionKind{Group: "cr.example.apiextensions.k8s.io", Version: "v1", Kind: "Example"}
// Get takes name of the example, and returns the corresponding example object, and an error if there is any.
func (c *FakeExamples) Get(name string, options v1.GetOptions) (result *crv1.Example, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(examplesResource, c.ns, name), &crv1.Example{})
if obj == nil {
return nil, err
}
return obj.(*crv1.Example), err
}
// List takes label and field selectors, and returns the list of Examples that match those selectors.
func (c *FakeExamples) List(opts v1.ListOptions) (result *crv1.ExampleList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(examplesResource, examplesKind, c.ns, opts), &crv1.ExampleList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &crv1.ExampleList{ListMeta: obj.(*crv1.ExampleList).ListMeta}
for _, item := range obj.(*crv1.ExampleList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested examples.
func (c *FakeExamples) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(examplesResource, c.ns, opts))
}
// Create takes the representation of a example and creates it. Returns the server's representation of the example, and an error, if there is any.
func (c *FakeExamples) Create(example *crv1.Example) (result *crv1.Example, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(examplesResource, c.ns, example), &crv1.Example{})
if obj == nil {
return nil, err
}
return obj.(*crv1.Example), err
}
// Update takes the representation of a example and updates it. Returns the server's representation of the example, and an error, if there is any.
func (c *FakeExamples) Update(example *crv1.Example) (result *crv1.Example, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(examplesResource, c.ns, example), &crv1.Example{})
if obj == nil {
return nil, err
}
return obj.(*crv1.Example), err
}
// Delete takes name of the example and deletes it. Returns an error if one occurs.
func (c *FakeExamples) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(examplesResource, c.ns, name), &crv1.Example{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeExamples) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(examplesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &crv1.ExampleList{})
return err
}
// Patch applies the patch and returns the patched example.
func (c *FakeExamples) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *crv1.Example, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(examplesResource, c.ns, name, data, subresources...), &crv1.Example{})
if obj == nil {
return nil, err
}
return obj.(*crv1.Example), err
}

View File

@ -0,0 +1,21 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
type ExampleExpansion interface{}

View File

@ -0,0 +1,46 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package cr
import (
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/informers/externalversions/cr/v1"
internalinterfaces "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to each of this group's versions.
type Interface interface {
// V1 provides access to shared informers for resources in V1.
V1() v1.Interface
}
type group struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// V1 returns a new v1.Interface.
func (g *group) V1() v1.Interface {
return v1.New(g.factory, g.namespace, g.tweakListOptions)
}

View File

@ -0,0 +1,89 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
time "time"
crv1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
versioned "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned"
internalinterfaces "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/informers/externalversions/internalinterfaces"
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/listers/cr/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// ExampleInformer provides access to a shared informer and lister for
// Examples.
type ExampleInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.ExampleLister
}
type exampleInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewExampleInformer constructs a new informer for Example type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewExampleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredExampleInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredExampleInformer constructs a new informer for Example type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredExampleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.CrV1().Examples(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.CrV1().Examples(namespace).Watch(options)
},
},
&crv1.Example{},
resyncPeriod,
indexers,
)
}
func (f *exampleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredExampleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *exampleInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&crv1.Example{}, f.defaultInformer)
}
func (f *exampleInformer) Lister() v1.ExampleLister {
return v1.NewExampleLister(f.Informer().GetIndexer())
}

View File

@ -0,0 +1,45 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
internalinterfaces "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/informers/externalversions/internalinterfaces"
)
// Interface provides access to all the informers in this group version.
type Interface interface {
// Examples returns a ExampleInformer.
Examples() ExampleInformer
}
type version struct {
factory internalinterfaces.SharedInformerFactory
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Examples returns a ExampleInformer.
func (v *version) Examples() ExampleInformer {
return &exampleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@ -0,0 +1,180 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
reflect "reflect"
sync "sync"
time "time"
versioned "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned"
cr "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/informers/externalversions/cr"
internalinterfaces "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/informers/externalversions/internalinterfaces"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// SharedInformerOption defines the functional option type for SharedInformerFactory.
type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
type sharedInformerFactory struct {
client versioned.Interface
namespace string
tweakListOptions internalinterfaces.TweakListOptionsFunc
lock sync.Mutex
defaultResync time.Duration
customResync map[reflect.Type]time.Duration
informers map[reflect.Type]cache.SharedIndexInformer
// startedInformers is used for tracking which informers have been started.
// This allows Start() to be called multiple times safely.
startedInformers map[reflect.Type]bool
}
// WithCustomResyncConfig sets a custom resync period for the specified informer types.
func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
for k, v := range resyncConfig {
factory.customResync[reflect.TypeOf(k)] = v
}
return factory
}
}
// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.tweakListOptions = tweakListOptions
return factory
}
}
// WithNamespace limits the SharedInformerFactory to the specified namespace.
func WithNamespace(namespace string) SharedInformerOption {
return func(factory *sharedInformerFactory) *sharedInformerFactory {
factory.namespace = namespace
return factory
}
}
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync)
}
// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
// Listers obtained via this SharedInformerFactory will be subject to the same filters
// as specified here.
// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
}
// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
factory := &sharedInformerFactory{
client: client,
namespace: v1.NamespaceAll,
defaultResync: defaultResync,
informers: make(map[reflect.Type]cache.SharedIndexInformer),
startedInformers: make(map[reflect.Type]bool),
customResync: make(map[reflect.Type]time.Duration),
}
// Apply all options
for _, opt := range options {
factory = opt(factory)
}
return factory
}
// Start initializes all requested informers.
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.lock.Lock()
defer f.lock.Unlock()
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
go informer.Run(stopCh)
f.startedInformers[informerType] = true
}
}
}
// WaitForCacheSync waits for all started informers' cache were synced.
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
informers := func() map[reflect.Type]cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informers := map[reflect.Type]cache.SharedIndexInformer{}
for informerType, informer := range f.informers {
if f.startedInformers[informerType] {
informers[informerType] = informer
}
}
return informers
}()
res := map[reflect.Type]bool{}
for informType, informer := range informers {
res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
}
return res
}
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
// client.
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
f.lock.Lock()
defer f.lock.Unlock()
informerType := reflect.TypeOf(obj)
informer, exists := f.informers[informerType]
if exists {
return informer
}
resyncPeriod, exists := f.customResync[informerType]
if !exists {
resyncPeriod = f.defaultResync
}
informer = newFunc(f.client, resyncPeriod)
f.informers[informerType] = informer
return informer
}
// SharedInformerFactory provides shared informers for resources in all known
// API group versions.
type SharedInformerFactory interface {
internalinterfaces.SharedInformerFactory
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
Cr() cr.Interface
}
func (f *sharedInformerFactory) Cr() cr.Interface {
return cr.New(f, f.namespace, f.tweakListOptions)
}

View File

@ -0,0 +1,62 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package externalversions
import (
"fmt"
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
cache "k8s.io/client-go/tools/cache"
)
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
// sharedInformers based on type
type GenericInformer interface {
Informer() cache.SharedIndexInformer
Lister() cache.GenericLister
}
type genericInformer struct {
informer cache.SharedIndexInformer
resource schema.GroupResource
}
// Informer returns the SharedIndexInformer.
func (f *genericInformer) Informer() cache.SharedIndexInformer {
return f.informer
}
// Lister returns the GenericLister.
func (f *genericInformer) Lister() cache.GenericLister {
return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
}
// ForResource gives generic access to a shared informer of the matching type
// TODO extend this to unknown resources with a client pool
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=cr.example.apiextensions.k8s.io, Version=v1
case v1.SchemeGroupVersion.WithResource("examples"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Cr().V1().Examples().Informer()}, nil
}
return nil, fmt.Errorf("no informer found for %v", resource)
}

View File

@ -0,0 +1,38 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package internalinterfaces
import (
time "time"
versioned "k8s.io/apiextensions-apiserver/examples/client-go/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
cache "k8s.io/client-go/tools/cache"
)
type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
type SharedInformerFactory interface {
Start(stopCh <-chan struct{})
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
}
type TweakListOptionsFunc func(*v1.ListOptions)

View File

@ -0,0 +1,94 @@
/*
Copyright 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "k8s.io/apiextensions-apiserver/examples/client-go/pkg/apis/cr/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ExampleLister helps list Examples.
type ExampleLister interface {
// List lists all Examples in the indexer.
List(selector labels.Selector) (ret []*v1.Example, err error)
// Examples returns an object that can list and get Examples.
Examples(namespace string) ExampleNamespaceLister
ExampleListerExpansion
}
// exampleLister implements the ExampleLister interface.
type exampleLister struct {
indexer cache.Indexer
}
// NewExampleLister returns a new ExampleLister.
func NewExampleLister(indexer cache.Indexer) ExampleLister {
return &exampleLister{indexer: indexer}
}
// List lists all Examples in the indexer.
func (s *exampleLister) List(selector labels.Selector) (ret []*v1.Example, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Example))
})
return ret, err
}
// Examples returns an object that can list and get Examples.
func (s *exampleLister) Examples(namespace string) ExampleNamespaceLister {
return exampleNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ExampleNamespaceLister helps list and get Examples.
type ExampleNamespaceLister interface {
// List lists all Examples in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1.Example, err error)
// Get retrieves the Example from the indexer for a given namespace and name.
Get(name string) (*v1.Example, error)
ExampleNamespaceListerExpansion
}
// exampleNamespaceLister implements the ExampleNamespaceLister
// interface.
type exampleNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Examples in the indexer for a given namespace.
func (s exampleNamespaceLister) List(selector labels.Selector) (ret []*v1.Example, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Example))
})
return ret, err
}
// Get retrieves the Example from the indexer for a given namespace and name.
func (s exampleNamespaceLister) Get(name string) (*v1.Example, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("example"), name)
}
return obj.(*v1.Example), nil
}

View File

@ -0,0 +1,27 @@
/*
Copyright 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
// ExampleListerExpansion allows custom methods to be added to
// ExampleLister.
type ExampleListerExpansion interface{}
// ExampleNamespaceListerExpansion allows custom methods to be added to
// ExampleNamespaceLister.
type ExampleNamespaceListerExpansion interface{}

View File

@ -0,0 +1,16 @@
/*
Copyright 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.
*/

30
vendor/k8s.io/apiextensions-apiserver/hack/build-image.sh generated vendored Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# 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.
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../../../../..
source "${KUBE_ROOT}/hack/lib/util.sh"
# Register function to be called on EXIT to remove generated binary.
function cleanup {
rm "${KUBE_ROOT}/vendor/k8s.io/apiextensions-apiserver/artifacts/simple-image/apiextensions-apiserver"
}
trap cleanup EXIT
pushd "${KUBE_ROOT}/vendor/k8s.io/apiextensions-apiserver"
cp -v ../../../../_output/local/bin/linux/amd64/apiextensions-apiserver ./artifacts/simple-image/apiextensions-apiserver
docker build -t apiextensions-apiserver:latest ./artifacts/simple-image
popd

34
vendor/k8s.io/apiextensions-apiserver/hack/update-codegen.sh generated vendored Executable file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env bash
# 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.
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
# generate the code with:
# --output-base because this script should also be able to run inside the vendor dir of
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
# instead of the $GOPATH directly. For normal projects this can be dropped.
CLIENTSET_NAME_VERSIONED=clientset \
CLIENTSET_NAME_INTERNAL=internalclientset \
${CODEGEN_PKG}/generate-internal-groups.sh all \
k8s.io/apiextensions-apiserver/pkg/client k8s.io/apiextensions-apiserver/pkg/apis k8s.io/apiextensions-apiserver/pkg/apis \
"apiextensions:v1beta1" \
--output-base "$(dirname ${BASH_SOURCE})/../../.." \
--go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt

49
vendor/k8s.io/apiextensions-apiserver/hack/verify-codegen.sh generated vendored Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env bash
# 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.
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
SCRIPT_BASE=${SCRIPT_ROOT}/../..
DIFFROOT="${SCRIPT_ROOT}/pkg"
TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg"
_tmp="${SCRIPT_ROOT}/_tmp"
cleanup() {
rm -rf "${_tmp}"
}
trap "cleanup" EXIT SIGINT
cleanup
mkdir -p "${TMP_DIFFROOT}"
cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}"
"${SCRIPT_ROOT}/hack/update-codegen.sh"
echo "diffing ${DIFFROOT} against freshly generated codegen"
ret=0
diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$?
cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}"
if [[ $ret -eq 0 ]]
then
echo "${DIFFROOT} up to date."
else
echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh"
exit 1
fi

40
vendor/k8s.io/apiextensions-apiserver/main.go generated vendored Normal file
View File

@ -0,0 +1,40 @@
/*
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 main
import (
"flag"
"os"
"github.com/golang/glog"
"k8s.io/apiextensions-apiserver/pkg/cmd/server"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/util/logs"
)
func main() {
logs.InitLogs()
defer logs.FlushLogs()
stopCh := genericapiserver.SetupSignalHandler()
cmd := server.NewServerCommand(os.Stdout, os.Stderr, stopCh)
cmd.Flags().AddGoFlagSet(flag.CommandLine)
if err := cmd.Execute(); err != nil {
glog.Fatal(err)
}
}

View File

@ -0,0 +1,262 @@
/*
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 apiextensions
import "k8s.io/apimachinery/pkg/runtime"
// TODO: Update this after a tag is created for interface fields in DeepCopy
func (in *JSONSchemaProps) DeepCopy() *JSONSchemaProps {
if in == nil {
return nil
}
out := new(JSONSchemaProps)
*out = *in
if in.Default != nil {
defaultJSON := JSON(runtime.DeepCopyJSONValue(*(in.Default)))
out.Default = &(defaultJSON)
} else {
out.Default = nil
}
if in.Example != nil {
exampleJSON := JSON(runtime.DeepCopyJSONValue(*(in.Example)))
out.Example = &(exampleJSON)
} else {
out.Example = nil
}
if in.Ref != nil {
in, out := &in.Ref, &out.Ref
if *in == nil {
*out = nil
} else {
*out = new(string)
**out = **in
}
}
if in.Maximum != nil {
in, out := &in.Maximum, &out.Maximum
if *in == nil {
*out = nil
} else {
*out = new(float64)
**out = **in
}
}
if in.Minimum != nil {
in, out := &in.Minimum, &out.Minimum
if *in == nil {
*out = nil
} else {
*out = new(float64)
**out = **in
}
}
if in.MaxLength != nil {
in, out := &in.MaxLength, &out.MaxLength
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MinLength != nil {
in, out := &in.MinLength, &out.MinLength
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MaxItems != nil {
in, out := &in.MaxItems, &out.MaxItems
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MinItems != nil {
in, out := &in.MinItems, &out.MinItems
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MultipleOf != nil {
in, out := &in.MultipleOf, &out.MultipleOf
if *in == nil {
*out = nil
} else {
*out = new(float64)
**out = **in
}
}
if in.Enum != nil {
out.Enum = make([]JSON, len(in.Enum))
for i := range in.Enum {
out.Enum[i] = runtime.DeepCopyJSONValue(in.Enum[i])
}
}
if in.MaxProperties != nil {
in, out := &in.MaxProperties, &out.MaxProperties
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MinProperties != nil {
in, out := &in.MinProperties, &out.MinProperties
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.Required != nil {
in, out := &in.Required, &out.Required
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Items != nil {
in, out := &in.Items, &out.Items
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaPropsOrArray)
(*in).DeepCopyInto(*out)
}
}
if in.AllOf != nil {
in, out := &in.AllOf, &out.AllOf
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.OneOf != nil {
in, out := &in.OneOf, &out.OneOf
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.AnyOf != nil {
in, out := &in.AnyOf, &out.AnyOf
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Not != nil {
in, out := &in.Not, &out.Not
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaProps)
(*in).DeepCopyInto(*out)
}
}
if in.Properties != nil {
in, out := &in.Properties, &out.Properties
*out = make(map[string]JSONSchemaProps, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.AdditionalProperties != nil {
in, out := &in.AdditionalProperties, &out.AdditionalProperties
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaPropsOrBool)
(*in).DeepCopyInto(*out)
}
}
if in.PatternProperties != nil {
in, out := &in.PatternProperties, &out.PatternProperties
*out = make(map[string]JSONSchemaProps, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.Dependencies != nil {
in, out := &in.Dependencies, &out.Dependencies
*out = make(JSONSchemaDependencies, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.AdditionalItems != nil {
in, out := &in.AdditionalItems, &out.AdditionalItems
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaPropsOrBool)
(*in).DeepCopyInto(*out)
}
}
if in.Definitions != nil {
in, out := &in.Definitions, &out.Definitions
*out = make(JSONSchemaDefinitions, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.ExternalDocs != nil {
in, out := &in.ExternalDocs, &out.ExternalDocs
if *in == nil {
*out = nil
} else {
*out = new(ExternalDocumentation)
(*in).DeepCopyInto(*out)
}
}
return out
}

View File

@ -0,0 +1,21 @@
/*
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
// Package apiextensions is the internal version of the API.
// +groupName=apiextensions.k8s.io
package apiextensions // import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"

View File

@ -0,0 +1,142 @@
/*
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 fuzzer
import (
"reflect"
"strings"
"github.com/google/gofuzz"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
// Funcs returns the fuzzer functions for the apiextensions apis.
func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
func(obj *apiextensions.CustomResourceDefinitionSpec, c fuzz.Continue) {
c.FuzzNoCustom(obj)
// match our defaulter
if len(obj.Scope) == 0 {
obj.Scope = apiextensions.NamespaceScoped
}
if len(obj.Names.Singular) == 0 {
obj.Names.Singular = strings.ToLower(obj.Names.Kind)
}
if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
obj.Names.ListKind = obj.Names.Kind + "List"
}
if len(obj.Versions) == 0 && len(obj.Version) != 0 {
obj.Versions = []apiextensions.CustomResourceDefinitionVersion{
{
Name: obj.Version,
Served: true,
Storage: true,
},
}
} else if len(obj.Versions) != 0 {
obj.Version = obj.Versions[0].Name
}
if len(obj.AdditionalPrinterColumns) == 0 {
obj.AdditionalPrinterColumns = []apiextensions.CustomResourceColumnDefinition{
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
}
}
},
func(obj *apiextensions.CustomResourceDefinition, c fuzz.Continue) {
c.FuzzNoCustom(obj)
if len(obj.Status.StoredVersions) == 0 {
for _, v := range obj.Spec.Versions {
if v.Storage && !apiextensions.IsStoredVersion(obj, v.Name) {
obj.Status.StoredVersions = append(obj.Status.StoredVersions, v.Name)
}
}
}
},
func(obj *apiextensions.JSONSchemaProps, c fuzz.Continue) {
// we cannot use c.FuzzNoCustom because of the interface{} fields. So let's loop with reflection.
vobj := reflect.ValueOf(obj).Elem()
tobj := reflect.TypeOf(obj).Elem()
for i := 0; i < tobj.NumField(); i++ {
field := tobj.Field(i)
switch field.Name {
case "Default", "Enum", "Example", "Ref":
continue
default:
isValue := true
switch field.Type.Kind() {
case reflect.Interface, reflect.Map, reflect.Slice, reflect.Ptr:
isValue = false
}
if isValue || c.Intn(10) == 0 {
c.Fuzz(vobj.Field(i).Addr().Interface())
}
}
}
if c.RandBool() {
validJSON := apiextensions.JSON(`{"some": {"json": "test"}, "string": 42}`)
obj.Default = &validJSON
}
if c.RandBool() {
obj.Enum = []apiextensions.JSON{c.Float64(), c.RandString(), c.RandBool()}
}
if c.RandBool() {
validJSON := apiextensions.JSON(`"foobarbaz"`)
obj.Example = &validJSON
}
if c.RandBool() {
validRef := "validRef"
obj.Ref = &validRef
}
},
func(obj *apiextensions.JSONSchemaPropsOrBool, c fuzz.Continue) {
if c.RandBool() {
obj.Allows = true
obj.Schema = &apiextensions.JSONSchemaProps{}
c.Fuzz(obj.Schema)
} else {
obj.Allows = c.RandBool()
}
},
func(obj *apiextensions.JSONSchemaPropsOrArray, c fuzz.Continue) {
// disallow both Schema and JSONSchemas to be nil.
if c.RandBool() {
obj.Schema = &apiextensions.JSONSchemaProps{}
c.Fuzz(obj.Schema)
} else {
obj.JSONSchemas = make([]apiextensions.JSONSchemaProps, c.Intn(3)+1)
for i := range obj.JSONSchemas {
c.Fuzz(&obj.JSONSchemas[i])
}
}
},
func(obj *apiextensions.JSONSchemaPropsOrStringArray, c fuzz.Continue) {
if c.RandBool() {
obj.Schema = &apiextensions.JSONSchemaProps{}
c.Fuzz(obj.Schema)
} else {
c.Fuzz(&obj.Property)
}
},
}
}

View File

@ -0,0 +1,149 @@
/*
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 apiextensions
import (
"fmt"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SetCRDCondition sets the status condition. It either overwrites the existing one or
// creates a new one
func SetCRDCondition(crd *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) {
existingCondition := FindCRDCondition(crd, newCondition.Type)
if existingCondition == nil {
newCondition.LastTransitionTime = metav1.NewTime(time.Now())
crd.Status.Conditions = append(crd.Status.Conditions, newCondition)
return
}
if existingCondition.Status != newCondition.Status {
existingCondition.Status = newCondition.Status
existingCondition.LastTransitionTime = newCondition.LastTransitionTime
}
existingCondition.Reason = newCondition.Reason
existingCondition.Message = newCondition.Message
}
// RemoveCRDCondition removes the status condition.
func RemoveCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) {
newConditions := []CustomResourceDefinitionCondition{}
for _, condition := range crd.Status.Conditions {
if condition.Type != conditionType {
newConditions = append(newConditions, condition)
}
}
crd.Status.Conditions = newConditions
}
// FindCRDCondition returns the condition you're looking for or nil
func FindCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition {
for i := range crd.Status.Conditions {
if crd.Status.Conditions[i].Type == conditionType {
return &crd.Status.Conditions[i]
}
}
return nil
}
// IsCRDConditionTrue indicates if the condition is present and strictly true
func IsCRDConditionTrue(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionTrue)
}
// IsCRDConditionFalse indicates if the condition is present and false true
func IsCRDConditionFalse(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionFalse)
}
// IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the arg
func IsCRDConditionPresentAndEqual(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool {
for _, condition := range crd.Status.Conditions {
if condition.Type == conditionType {
return condition.Status == status
}
}
return false
}
// IsCRDConditionEquivalent returns true if the lhs and rhs are equivalent except for times
func IsCRDConditionEquivalent(lhs, rhs *CustomResourceDefinitionCondition) bool {
if lhs == nil && rhs == nil {
return true
}
if lhs == nil || rhs == nil {
return false
}
return lhs.Message == rhs.Message && lhs.Reason == rhs.Reason && lhs.Status == rhs.Status && lhs.Type == rhs.Type
}
// CRDHasFinalizer returns true if the finalizer is in the list
func CRDHasFinalizer(crd *CustomResourceDefinition, needle string) bool {
for _, finalizer := range crd.Finalizers {
if finalizer == needle {
return true
}
}
return false
}
// CRDRemoveFinalizer removes the finalizer if present
func CRDRemoveFinalizer(crd *CustomResourceDefinition, needle string) {
newFinalizers := []string{}
for _, finalizer := range crd.Finalizers {
if finalizer != needle {
newFinalizers = append(newFinalizers, finalizer)
}
}
crd.Finalizers = newFinalizers
}
// HasServedCRDVersion returns true if `version` is in the list of CRD's versions and the Served flag is set.
func HasServedCRDVersion(crd *CustomResourceDefinition, version string) bool {
for _, v := range crd.Spec.Versions {
if v.Name == version {
return v.Served
}
}
return false
}
// GetCRDStorageVersion returns the storage version for given CRD.
func GetCRDStorageVersion(crd *CustomResourceDefinition) (string, error) {
for _, v := range crd.Spec.Versions {
if v.Storage {
return v.Name, nil
}
}
// This should not happened if crd is valid
return "", fmt.Errorf("invalid CustomResourceDefinition, no storage version")
}
func IsStoredVersion(crd *CustomResourceDefinition, version string) bool {
for _, v := range crd.Status.StoredVersions {
if version == v {
return true
}
}
return false
}

View File

@ -0,0 +1,421 @@
/*
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 apiextensions
import (
"reflect"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestCRDHasFinalizer(t *testing.T) {
tests := []struct {
name string
crd *CustomResourceDefinition
finalizerToCheck string
expected bool
}{
{
name: "missing",
crd: &CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it"}},
},
finalizerToCheck: "it",
expected: false,
},
{
name: "present",
crd: &CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it", "it"}},
},
finalizerToCheck: "it",
expected: true,
},
}
for _, tc := range tests {
actual := CRDHasFinalizer(tc.crd, tc.finalizerToCheck)
if tc.expected != actual {
t.Errorf("%v expected %v, got %v", tc.name, tc.expected, actual)
}
}
}
func TestCRDRemoveFinalizer(t *testing.T) {
tests := []struct {
name string
crd *CustomResourceDefinition
finalizerToCheck string
expected []string
}{
{
name: "missing",
crd: &CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it"}},
},
finalizerToCheck: "it",
expected: []string{"not-it"},
},
{
name: "present",
crd: &CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Finalizers: []string{"not-it", "it"}},
},
finalizerToCheck: "it",
expected: []string{"not-it"},
},
}
for _, tc := range tests {
CRDRemoveFinalizer(tc.crd, tc.finalizerToCheck)
if !reflect.DeepEqual(tc.expected, tc.crd.Finalizers) {
t.Errorf("%v expected %v, got %v", tc.name, tc.expected, tc.crd.Finalizers)
}
}
}
func TestSetCRDCondition(t *testing.T) {
tests := []struct {
name string
crdCondition []CustomResourceDefinitionCondition
newCondition CustomResourceDefinitionCondition
expectedcrdCondition []CustomResourceDefinitionCondition
}{
{
name: "test setCRDcondition when one condition",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
newCondition: CustomResourceDefinitionCondition{
Type: Established,
Status: ConditionFalse,
Reason: "NotAccepted",
Message: "Not accepted",
LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
},
expectedcrdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionFalse,
Reason: "NotAccepted",
Message: "Not accepted",
LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
},
},
},
{
name: "test setCRDcondition when two condition",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
newCondition: CustomResourceDefinitionCondition{
Type: NamesAccepted,
Status: ConditionFalse,
Reason: "Conflicts",
Message: "conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
},
expectedcrdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionFalse,
Reason: "Conflicts",
Message: "conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC),
},
},
},
{
name: "test setCRDcondition when condition needs to be appended",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
newCondition: CustomResourceDefinitionCondition{
Type: Terminating,
Status: ConditionFalse,
Reason: "NeverEstablished",
Message: "resource was never established",
LastTransitionTime: metav1.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC),
},
expectedcrdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: Terminating,
Status: ConditionFalse,
Reason: "NeverEstablished",
Message: "resource was never established",
LastTransitionTime: metav1.Date(2018, 2, 1, 0, 0, 0, 0, time.UTC),
},
},
},
}
for _, tc := range tests {
crd := generateCRDwithCondition(tc.crdCondition)
SetCRDCondition(crd, tc.newCondition)
if len(tc.expectedcrdCondition) != len(crd.Status.Conditions) {
t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
}
for i := range tc.expectedcrdCondition {
if !IsCRDConditionEquivalent(&tc.expectedcrdCondition[i], &crd.Status.Conditions[i]) {
t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
}
}
}
}
func TestRemoveCRDCondition(t *testing.T) {
tests := []struct {
name string
crdCondition []CustomResourceDefinitionCondition
conditionType CustomResourceDefinitionConditionType
expectedcrdCondition []CustomResourceDefinitionCondition
}{
{
name: "test remove CRDCondition when the conditionType meets",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
conditionType: NamesAccepted,
expectedcrdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2011, 1, 2, 0, 0, 0, 0, time.UTC),
},
},
},
{
name: "test remove CRDCondition when the conditionType not meets",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
conditionType: Terminating,
expectedcrdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
},
}
for _, tc := range tests {
crd := generateCRDwithCondition(tc.crdCondition)
RemoveCRDCondition(crd, tc.conditionType)
if len(tc.expectedcrdCondition) != len(crd.Status.Conditions) {
t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
}
for i := range tc.expectedcrdCondition {
if !IsCRDConditionEquivalent(&tc.expectedcrdCondition[i], &crd.Status.Conditions[i]) {
t.Errorf("%v expected %v, got %v", tc.name, tc.expectedcrdCondition, crd.Status.Conditions)
}
}
}
}
func TestIsCRDConditionPresentAndEqual(t *testing.T) {
tests := []struct {
name string
crdCondition []CustomResourceDefinitionCondition
conditionType CustomResourceDefinitionConditionType
status ConditionStatus
expectresult bool
}{
{
name: "test CRDCondition is not Present",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
conditionType: Terminating,
status: ConditionTrue,
expectresult: false,
},
{
name: "test CRDCondition is Present but not Equal",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
conditionType: Established,
status: ConditionFalse,
expectresult: false,
},
{
name: "test CRDCondition is Present and Equal",
crdCondition: []CustomResourceDefinitionCondition{
{
Type: Established,
Status: ConditionTrue,
Reason: "Accepted",
Message: "the initial names have been accepted",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
Type: NamesAccepted,
Status: ConditionTrue,
Reason: "NoConflicts",
Message: "no conflicts found",
LastTransitionTime: metav1.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
conditionType: NamesAccepted,
status: ConditionTrue,
expectresult: true,
},
}
for _, tc := range tests {
crd := generateCRDwithCondition(tc.crdCondition)
res := IsCRDConditionPresentAndEqual(crd, tc.conditionType, tc.status)
if res != tc.expectresult {
t.Errorf("%v expected %t, got %t", tc.name, tc.expectresult, res)
}
}
}
func generateCRDwithCondition(conditions []CustomResourceDefinitionCondition) *CustomResourceDefinition {
testCRDObjectMeta := metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "12",
}
testCRDSpec := CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Scope: ResourceScope("Cluster"),
Names: CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "kind",
ListKind: "listkind",
},
}
testCRDAcceptedNames := CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "kind",
ListKind: "listkind",
}
return &CustomResourceDefinition{
ObjectMeta: testCRDObjectMeta,
Spec: testCRDSpec,
Status: CustomResourceDefinitionStatus{
AcceptedNames: testCRDAcceptedNames,
Conditions: conditions,
},
}
}

View File

@ -0,0 +1,31 @@
/*
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 install
import (
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
utilruntime.Must(apiextensions.AddToScheme(scheme))
utilruntime.Must(v1beta1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion))
}

View File

@ -0,0 +1,28 @@
/*
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 install
import (
"testing"
apiextensionsfuzzer "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer"
"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
)
func TestRoundTrip(t *testing.T) {
roundtrip.RoundTripTestForAPIGroup(t, Install, apiextensionsfuzzer.Funcs)
}

View File

@ -0,0 +1,51 @@
/*
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 apiextensions
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const GroupName = "apiextensions.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns back a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&CustomResourceDefinition{},
&CustomResourceDefinitionList{},
)
return nil
}

View File

@ -0,0 +1,250 @@
/*
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 apiextensions
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// CustomResourceDefinitionSpec describes how a user wants their resource to appear
type CustomResourceDefinitionSpec struct {
// Group is the group this resource belongs in
Group string
// Version is the version this resource belongs in
// Should be always first item in Versions field if provided.
// Optional, but at least one of Version or Versions must be set.
// Deprecated: Please use `Versions`.
Version string
// Names are the names used to describe this custom resource
Names CustomResourceDefinitionNames
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
Scope ResourceScope
// Validation describes the validation methods for CustomResources
Validation *CustomResourceValidation
// Subresources describes the subresources for CustomResources
Subresources *CustomResourceSubresources
// Versions is the list of all supported versions for this resource.
// If Version field is provided, this field is optional.
// Validation: All versions must use the same validation schema for now. i.e., top
// level Validation field is applied to all of these versions.
// Order: The version name will be used to compute the order.
// If the version string is "kube-like", it will sort above non "kube-like" version strings, which are ordered
// lexicographically. "Kube-like" versions start with a "v", then are followed by a number (the major version),
// then optionally the string "alpha" or "beta" and another number (the minor version). These are sorted first
// by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing
// major version, then minor version. An example sorted list of versions:
// v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.
Versions []CustomResourceDefinitionVersion
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
AdditionalPrinterColumns []CustomResourceColumnDefinition
}
type CustomResourceDefinitionVersion struct {
// Name is the version name, e.g. “v1”, “v2beta1”, etc.
Name string
// Served is a flag enabling/disabling this version from being served via REST APIs
Served bool
// Storage flags the version as storage version. There must be exactly one flagged
// as storage version.
Storage bool
}
// CustomResourceColumnDefinition specifies a column for server side printing.
type CustomResourceColumnDefinition struct {
// name is a human readable name for the column.
Name string
// type is an OpenAPI type definition for this column.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.
Type string
// format is an optional OpenAPI type definition for this column. The 'name' format is applied
// to the primary identifier column to assist in clients identifying column is the resource name.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.
Format string
// description is a human readable description of this column.
Description string
// priority is an integer defining the relative importance of this column compared to others. Lower
// numbers are considered higher priority. Columns that may be omitted in limited space scenarios
// should be given a higher priority.
Priority int32
// JSONPath is a simple JSON path, i.e. without array notation.
JSONPath string
}
// CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition
type CustomResourceDefinitionNames struct {
// Plural is the plural name of the resource to serve. It must match the name of the CustomResourceDefinition-registration
// too: plural.group and it must be all lowercase.
Plural string
// Singular is the singular name of the resource. It must be all lowercase Defaults to lowercased <kind>
Singular string
// ShortNames are short names for the resource. It must be all lowercase.
ShortNames []string
// Kind is the serialized kind of the resource. It is normally CamelCase and singular.
Kind string
// ListKind is the serialized kind of the list for this resource. Defaults to <kind>List.
ListKind string
// Categories is a list of grouped resources custom resources belong to (e.g. 'all')
// +optional
Categories []string
}
// ResourceScope is an enum defining the different scopes available to a custom resource
type ResourceScope string
const (
ClusterScoped ResourceScope = "Cluster"
NamespaceScoped ResourceScope = "Namespaced"
)
type ConditionStatus string
// These are valid condition statuses. "ConditionTrue" means a resource is in the condition.
// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes
// can't decide if a resource is in the condition or not. In the future, we could add other
// intermediate conditions, e.g. ConditionDegraded.
const (
ConditionTrue ConditionStatus = "True"
ConditionFalse ConditionStatus = "False"
ConditionUnknown ConditionStatus = "Unknown"
)
// CustomResourceDefinitionConditionType is a valid value for CustomResourceDefinitionCondition.Type
type CustomResourceDefinitionConditionType string
const (
// Established means that the resource has become active. A resource is established when all names are
// accepted without a conflict for the first time. A resource stays established until deleted, even during
// a later NamesAccepted due to changed names. Note that not all names can be changed.
Established CustomResourceDefinitionConditionType = "Established"
// NamesAccepted means the names chosen for this CustomResourceDefinition do not conflict with others in
// the group and are therefore accepted.
NamesAccepted CustomResourceDefinitionConditionType = "NamesAccepted"
// Terminating means that the CustomResourceDefinition has been deleted and is cleaning up.
Terminating CustomResourceDefinitionConditionType = "Terminating"
)
// CustomResourceDefinitionCondition contains details for the current condition of this pod.
type CustomResourceDefinitionCondition struct {
// Type is the type of the condition.
Type CustomResourceDefinitionConditionType
// Status is the status of the condition.
// Can be True, False, Unknown.
Status ConditionStatus
// Last time the condition transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time
// Unique, one-word, CamelCase reason for the condition's last transition.
// +optional
Reason string
// Human-readable message indicating details about last transition.
// +optional
Message string
}
// CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition
type CustomResourceDefinitionStatus struct {
// Conditions indicate state for particular aspects of a CustomResourceDefinition
Conditions []CustomResourceDefinitionCondition
// AcceptedNames are the names that are actually being used to serve discovery
// They may be different than the names in spec.
AcceptedNames CustomResourceDefinitionNames
// StoredVersions are all versions of CustomResources that were ever persisted. Tracking these
// versions allows a migration path for stored versions in etcd. The field is mutable
// so the migration controller can first finish a migration to another version (i.e.
// that no old objects are left in the storage), and then remove the rest of the
// versions from this list.
// None of the versions in this list can be removed from the spec.Versions field.
StoredVersions []string
}
// CustomResourceCleanupFinalizer is the name of the finalizer which will delete instances of
// a CustomResourceDefinition
const CustomResourceCleanupFinalizer = "customresourcecleanup.apiextensions.k8s.io"
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format
// <.spec.name>.<.spec.group>.
type CustomResourceDefinition struct {
metav1.TypeMeta
metav1.ObjectMeta
// Spec describes how the user wants the resources to appear
Spec CustomResourceDefinitionSpec
// Status indicates the actual state of the CustomResourceDefinition
Status CustomResourceDefinitionStatus
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CustomResourceDefinitionList is a list of CustomResourceDefinition objects.
type CustomResourceDefinitionList struct {
metav1.TypeMeta
metav1.ListMeta
// Items individual CustomResourceDefinitions
Items []CustomResourceDefinition
}
// CustomResourceValidation is a list of validation methods for CustomResources.
type CustomResourceValidation struct {
// OpenAPIV3Schema is the OpenAPI v3 schema to be validated against.
OpenAPIV3Schema *JSONSchemaProps
}
// CustomResourceSubresources defines the status and scale subresources for CustomResources.
type CustomResourceSubresources struct {
// Status denotes the status subresource for CustomResources
Status *CustomResourceSubresourceStatus
// Scale denotes the scale subresource for CustomResources
Scale *CustomResourceSubresourceScale
}
// CustomResourceSubresourceStatus defines how to serve the status subresource for CustomResources.
// Status is represented by the `.status` JSON path inside of a CustomResource. When set,
// * exposes a /status subresource for the custom resource
// * PUT requests to the /status subresource take a custom resource object, and ignore changes to anything except the status stanza
// * PUT/POST/PATCH requests to the custom resource ignore changes to the status stanza
type CustomResourceSubresourceStatus struct{}
// CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.
type CustomResourceSubresourceScale struct {
// SpecReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Spec.Replicas.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .spec.
// If there is no value under the given path in the CustomResource, the /scale subresource will return an error on GET.
SpecReplicasPath string
// StatusReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Replicas.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .status.
// If there is no value under the given path in the CustomResource, the status replica value in the /scale subresource
// will default to 0.
StatusReplicasPath string
// LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .status.
// Must be set to work with HPA.
// If there is no value under the given path in the CustomResource, the status label selector value in the /scale
// subresource will default to the empty string.
// +optional
LabelSelectorPath *string
}

View File

@ -0,0 +1,96 @@
/*
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 apiextensions
// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).
type JSONSchemaProps struct {
ID string
Schema JSONSchemaURL
Ref *string
Description string
Type string
Format string
Title string
Default *JSON
Maximum *float64
ExclusiveMaximum bool
Minimum *float64
ExclusiveMinimum bool
MaxLength *int64
MinLength *int64
Pattern string
MaxItems *int64
MinItems *int64
UniqueItems bool
MultipleOf *float64
Enum []JSON
MaxProperties *int64
MinProperties *int64
Required []string
Items *JSONSchemaPropsOrArray
AllOf []JSONSchemaProps
OneOf []JSONSchemaProps
AnyOf []JSONSchemaProps
Not *JSONSchemaProps
Properties map[string]JSONSchemaProps
AdditionalProperties *JSONSchemaPropsOrBool
PatternProperties map[string]JSONSchemaProps
Dependencies JSONSchemaDependencies
AdditionalItems *JSONSchemaPropsOrBool
Definitions JSONSchemaDefinitions
ExternalDocs *ExternalDocumentation
Example *JSON
}
// JSON represents any valid JSON value.
// These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil.
type JSON interface{}
// JSONSchemaURL represents a schema url.
type JSONSchemaURL string
// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps
// or an array of JSONSchemaProps. Mainly here for serialization purposes.
type JSONSchemaPropsOrArray struct {
Schema *JSONSchemaProps
JSONSchemas []JSONSchemaProps
}
// JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value.
// Defaults to true for the boolean property.
type JSONSchemaPropsOrBool struct {
Allows bool
Schema *JSONSchemaProps
}
// JSONSchemaDependencies represent a dependencies property.
type JSONSchemaDependencies map[string]JSONSchemaPropsOrStringArray
// JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array.
type JSONSchemaPropsOrStringArray struct {
Schema *JSONSchemaProps
Property []string
}
// JSONSchemaDefinitions contains the models explicitly defined in this spec.
type JSONSchemaDefinitions map[string]JSONSchemaProps
// ExternalDocumentation allows referencing an external resource for extended documentation.
type ExternalDocumentation struct {
Description string
URL string
}

View File

@ -0,0 +1,73 @@
/*
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 (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
)
func addConversionFuncs(scheme *runtime.Scheme) error {
// Add non-generated conversion functions
err := scheme.AddConversionFuncs(
Convert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps,
Convert_apiextensions_JSON_To_v1beta1_JSON,
Convert_v1beta1_JSON_To_apiextensions_JSON,
)
if err != nil {
return err
}
return nil
}
func Convert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in *apiextensions.JSONSchemaProps, out *JSONSchemaProps, s conversion.Scope) error {
if err := autoConvert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in, out, s); err != nil {
return err
}
if in.Default != nil && *(in.Default) == nil {
out.Default = nil
}
if in.Example != nil && *(in.Example) == nil {
out.Example = nil
}
return nil
}
func Convert_apiextensions_JSON_To_v1beta1_JSON(in *apiextensions.JSON, out *JSON, s conversion.Scope) error {
raw, err := json.Marshal(*in)
if err != nil {
return err
}
out.Raw = raw
return nil
}
func Convert_v1beta1_JSON_To_apiextensions_JSON(in *JSON, out *apiextensions.JSON, s conversion.Scope) error {
if in != nil {
var i interface{}
if err := json.Unmarshal(in.Raw, &i); err != nil {
return err
}
*out = i
} else {
out = nil
}
return nil
}

View File

@ -0,0 +1,113 @@
/*
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 (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
)
func TestJSONConversion(t *testing.T) {
nilJSON := apiextensions.JSON(nil)
nullJSON := apiextensions.JSON("null")
stringJSON := apiextensions.JSON("foo")
boolJSON := apiextensions.JSON(true)
sliceJSON := apiextensions.JSON([]string{"foo", "bar", "baz"})
testCases := map[string]struct {
input *apiextensions.JSONSchemaProps
expected *JSONSchemaProps
}{
"nil": {
input: &apiextensions.JSONSchemaProps{
Default: nil,
},
expected: &JSONSchemaProps{},
},
"aliased nil": {
input: &apiextensions.JSONSchemaProps{
Default: &nilJSON,
},
expected: &JSONSchemaProps{},
},
"null": {
input: &apiextensions.JSONSchemaProps{
Default: &nullJSON,
},
expected: &JSONSchemaProps{
Default: &JSON{
Raw: []byte(`"null"`),
},
},
},
"string": {
input: &apiextensions.JSONSchemaProps{
Default: &stringJSON,
},
expected: &JSONSchemaProps{
Default: &JSON{
Raw: []byte(`"foo"`),
},
},
},
"bool": {
input: &apiextensions.JSONSchemaProps{
Default: &boolJSON,
},
expected: &JSONSchemaProps{
Default: &JSON{
Raw: []byte(`true`),
},
},
},
"slice": {
input: &apiextensions.JSONSchemaProps{
Default: &sliceJSON,
},
expected: &JSONSchemaProps{
Default: &JSON{
Raw: []byte(`["foo","bar","baz"]`),
},
},
},
}
scheme := runtime.NewScheme()
// add internal and external types
if err := apiextensions.AddToScheme(scheme); err != nil {
t.Fatal(err)
}
if err := AddToScheme(scheme); err != nil {
t.Fatal(err)
}
for k, tc := range testCases {
external := &JSONSchemaProps{}
if err := scheme.Convert(tc.input, external, nil); err != nil {
t.Errorf("%s: unexpected error: %v", k, err)
}
if !reflect.DeepEqual(external, tc.expected) {
t.Errorf("%s: expected\n\t%#v, got \n\t%#v", k, tc.expected, external)
}
}
}

View File

@ -0,0 +1,238 @@
/*
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
// TODO: Update this after a tag is created for interface fields in DeepCopy
func (in *JSONSchemaProps) DeepCopy() *JSONSchemaProps {
if in == nil {
return nil
}
out := new(JSONSchemaProps)
*out = *in
if in.Ref != nil {
in, out := &in.Ref, &out.Ref
if *in == nil {
*out = nil
} else {
*out = new(string)
**out = **in
}
}
if in.Maximum != nil {
in, out := &in.Maximum, &out.Maximum
if *in == nil {
*out = nil
} else {
*out = new(float64)
**out = **in
}
}
if in.Minimum != nil {
in, out := &in.Minimum, &out.Minimum
if *in == nil {
*out = nil
} else {
*out = new(float64)
**out = **in
}
}
if in.MaxLength != nil {
in, out := &in.MaxLength, &out.MaxLength
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MinLength != nil {
in, out := &in.MinLength, &out.MinLength
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MaxItems != nil {
in, out := &in.MaxItems, &out.MaxItems
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MinItems != nil {
in, out := &in.MinItems, &out.MinItems
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MultipleOf != nil {
in, out := &in.MultipleOf, &out.MultipleOf
if *in == nil {
*out = nil
} else {
*out = new(float64)
**out = **in
}
}
if in.MaxProperties != nil {
in, out := &in.MaxProperties, &out.MaxProperties
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.MinProperties != nil {
in, out := &in.MinProperties, &out.MinProperties
if *in == nil {
*out = nil
} else {
*out = new(int64)
**out = **in
}
}
if in.Required != nil {
in, out := &in.Required, &out.Required
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Items != nil {
in, out := &in.Items, &out.Items
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaPropsOrArray)
(*in).DeepCopyInto(*out)
}
}
if in.AllOf != nil {
in, out := &in.AllOf, &out.AllOf
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.OneOf != nil {
in, out := &in.OneOf, &out.OneOf
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.AnyOf != nil {
in, out := &in.AnyOf, &out.AnyOf
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Not != nil {
in, out := &in.Not, &out.Not
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaProps)
(*in).DeepCopyInto(*out)
}
}
if in.Properties != nil {
in, out := &in.Properties, &out.Properties
*out = make(map[string]JSONSchemaProps, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.AdditionalProperties != nil {
in, out := &in.AdditionalProperties, &out.AdditionalProperties
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaPropsOrBool)
(*in).DeepCopyInto(*out)
}
}
if in.PatternProperties != nil {
in, out := &in.PatternProperties, &out.PatternProperties
*out = make(map[string]JSONSchemaProps, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.Dependencies != nil {
in, out := &in.Dependencies, &out.Dependencies
*out = make(JSONSchemaDependencies, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.AdditionalItems != nil {
in, out := &in.AdditionalItems, &out.AdditionalItems
if *in == nil {
*out = nil
} else {
*out = new(JSONSchemaPropsOrBool)
(*in).DeepCopyInto(*out)
}
}
if in.Definitions != nil {
in, out := &in.Definitions, &out.Definitions
*out = make(JSONSchemaDefinitions, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
if in.ExternalDocs != nil {
in, out := &in.ExternalDocs, &out.ExternalDocs
if *in == nil {
*out = nil
} else {
*out = new(ExternalDocumentation)
(*in).DeepCopyInto(*out)
}
}
return out
}

View File

@ -0,0 +1,74 @@
/*
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 (
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
func addDefaultingFuncs(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&CustomResourceDefinition{}, func(obj interface{}) { SetDefaults_CustomResourceDefinition(obj.(*CustomResourceDefinition)) })
// TODO figure out why I can't seem to get my defaulter generated
// return RegisterDefaults(scheme)
return nil
}
func SetDefaults_CustomResourceDefinition(obj *CustomResourceDefinition) {
SetDefaults_CustomResourceDefinitionSpec(&obj.Spec)
if len(obj.Status.StoredVersions) == 0 {
for _, v := range obj.Spec.Versions {
if v.Storage {
obj.Status.StoredVersions = append(obj.Status.StoredVersions, v.Name)
break
}
}
}
}
func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec) {
if len(obj.Scope) == 0 {
obj.Scope = NamespaceScoped
}
if len(obj.Names.Singular) == 0 {
obj.Names.Singular = strings.ToLower(obj.Names.Kind)
}
if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
obj.Names.ListKind = obj.Names.Kind + "List"
}
// If there is no list of versions, create on using deprecated Version field.
if len(obj.Versions) == 0 && len(obj.Version) != 0 {
obj.Versions = []CustomResourceDefinitionVersion{{
Name: obj.Version,
Storage: true,
Served: true,
}}
}
// For backward compatibility set the version field to the first item in versions list.
if len(obj.Version) == 0 && len(obj.Versions) != 0 {
obj.Version = obj.Versions[0].Name
}
if len(obj.AdditionalPrinterColumns) == 0 {
obj.AdditionalPrinterColumns = []CustomResourceColumnDefinition{
{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
}
}
}

View File

@ -0,0 +1,24 @@
/*
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
// +k8s:conversion-gen=k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
// +k8s:defaulter-gen=TypeMeta
// Package v1beta1 is the v1beta1 version of the API.
// +groupName=apiextensions.k8s.io
// +k8s:openapi-gen=true
package v1beta1 // import "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,365 @@
/*
Copyright 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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = 'proto2';
package k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1beta1;
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1beta1";
// CustomResourceColumnDefinition specifies a column for server side printing.
message CustomResourceColumnDefinition {
// name is a human readable name for the column.
optional string name = 1;
// type is an OpenAPI type definition for this column.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.
optional string type = 2;
// format is an optional OpenAPI type definition for this column. The 'name' format is applied
// to the primary identifier column to assist in clients identifying column is the resource name.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.
// +optional
optional string format = 3;
// description is a human readable description of this column.
// +optional
optional string description = 4;
// priority is an integer defining the relative importance of this column compared to others. Lower
// numbers are considered higher priority. Columns that may be omitted in limited space scenarios
// should be given a higher priority.
// +optional
optional int32 priority = 5;
// JSONPath is a simple JSON path, i.e. with array notation.
optional string JSONPath = 6;
}
// CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format
// <.spec.name>.<.spec.group>.
message CustomResourceDefinition {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Spec describes how the user wants the resources to appear
optional CustomResourceDefinitionSpec spec = 2;
// Status indicates the actual state of the CustomResourceDefinition
// +optional
optional CustomResourceDefinitionStatus status = 3;
}
// CustomResourceDefinitionCondition contains details for the current condition of this pod.
message CustomResourceDefinitionCondition {
// Type is the type of the condition.
optional string type = 1;
// Status is the status of the condition.
// Can be True, False, Unknown.
optional string status = 2;
// Last time the condition transitioned from one status to another.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 3;
// Unique, one-word, CamelCase reason for the condition's last transition.
// +optional
optional string reason = 4;
// Human-readable message indicating details about last transition.
// +optional
optional string message = 5;
}
// CustomResourceDefinitionList is a list of CustomResourceDefinition objects.
message CustomResourceDefinitionList {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
// Items individual CustomResourceDefinitions
repeated CustomResourceDefinition items = 2;
}
// CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition
message CustomResourceDefinitionNames {
// Plural is the plural name of the resource to serve. It must match the name of the CustomResourceDefinition-registration
// too: plural.group and it must be all lowercase.
optional string plural = 1;
// Singular is the singular name of the resource. It must be all lowercase Defaults to lowercased <kind>
// +optional
optional string singular = 2;
// ShortNames are short names for the resource. It must be all lowercase.
// +optional
repeated string shortNames = 3;
// Kind is the serialized kind of the resource. It is normally CamelCase and singular.
optional string kind = 4;
// ListKind is the serialized kind of the list for this resource. Defaults to <kind>List.
// +optional
optional string listKind = 5;
// Categories is a list of grouped resources custom resources belong to (e.g. 'all')
// +optional
repeated string categories = 6;
}
// CustomResourceDefinitionSpec describes how a user wants their resource to appear
message CustomResourceDefinitionSpec {
// Group is the group this resource belongs in
optional string group = 1;
// Version is the version this resource belongs in
// Should be always first item in Versions field if provided.
// Optional, but at least one of Version or Versions must be set.
// Deprecated: Please use `Versions`.
// +optional
optional string version = 2;
// Names are the names used to describe this custom resource
optional CustomResourceDefinitionNames names = 3;
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
optional string scope = 4;
// Validation describes the validation methods for CustomResources
// +optional
optional CustomResourceValidation validation = 5;
// Subresources describes the subresources for CustomResources
// +optional
optional CustomResourceSubresources subresources = 6;
// Versions is the list of all supported versions for this resource.
// If Version field is provided, this field is optional.
// Validation: All versions must use the same validation schema for now. i.e., top
// level Validation field is applied to all of these versions.
// Order: The version name will be used to compute the order.
// If the version string is "kube-like", it will sort above non "kube-like" version strings, which are ordered
// lexicographically. "Kube-like" versions start with a "v", then are followed by a number (the major version),
// then optionally the string "alpha" or "beta" and another number (the minor version). These are sorted first
// by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing
// major version, then minor version. An example sorted list of versions:
// v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.
// +optional
repeated CustomResourceDefinitionVersion versions = 7;
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
// +optional
repeated CustomResourceColumnDefinition additionalPrinterColumns = 8;
}
// CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition
message CustomResourceDefinitionStatus {
// Conditions indicate state for particular aspects of a CustomResourceDefinition
repeated CustomResourceDefinitionCondition conditions = 1;
// AcceptedNames are the names that are actually being used to serve discovery
// They may be different than the names in spec.
optional CustomResourceDefinitionNames acceptedNames = 2;
// StoredVersions are all versions of CustomResources that were ever persisted. Tracking these
// versions allows a migration path for stored versions in etcd. The field is mutable
// so the migration controller can first finish a migration to another version (i.e.
// that no old objects are left in the storage), and then remove the rest of the
// versions from this list.
// None of the versions in this list can be removed from the spec.Versions field.
repeated string storedVersions = 3;
}
message CustomResourceDefinitionVersion {
// Name is the version name, e.g. v1, v2beta1, etc.
optional string name = 1;
// Served is a flag enabling/disabling this version from being served via REST APIs
optional bool served = 2;
// Storage flags the version as storage version. There must be exactly one
// flagged as storage version.
optional bool storage = 3;
}
// CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.
message CustomResourceSubresourceScale {
// SpecReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Spec.Replicas.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .spec.
// If there is no value under the given path in the CustomResource, the /scale subresource will return an error on GET.
optional string specReplicasPath = 1;
// StatusReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Replicas.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .status.
// If there is no value under the given path in the CustomResource, the status replica value in the /scale subresource
// will default to 0.
optional string statusReplicasPath = 2;
// LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .status.
// Must be set to work with HPA.
// If there is no value under the given path in the CustomResource, the status label selector value in the /scale
// subresource will default to the empty string.
// +optional
optional string labelSelectorPath = 3;
}
// CustomResourceSubresourceStatus defines how to serve the status subresource for CustomResources.
// Status is represented by the `.status` JSON path inside of a CustomResource. When set,
// * exposes a /status subresource for the custom resource
// * PUT requests to the /status subresource take a custom resource object, and ignore changes to anything except the status stanza
// * PUT/POST/PATCH requests to the custom resource ignore changes to the status stanza
message CustomResourceSubresourceStatus {
}
// CustomResourceSubresources defines the status and scale subresources for CustomResources.
message CustomResourceSubresources {
// Status denotes the status subresource for CustomResources
// +optional
optional CustomResourceSubresourceStatus status = 1;
// Scale denotes the scale subresource for CustomResources
// +optional
optional CustomResourceSubresourceScale scale = 2;
}
// CustomResourceValidation is a list of validation methods for CustomResources.
message CustomResourceValidation {
// OpenAPIV3Schema is the OpenAPI v3 schema to be validated against.
// +optional
optional JSONSchemaProps openAPIV3Schema = 1;
}
// ExternalDocumentation allows referencing an external resource for extended documentation.
message ExternalDocumentation {
optional string description = 1;
optional string url = 2;
}
// JSON represents any valid JSON value.
// These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil.
message JSON {
optional bytes raw = 1;
}
// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).
message JSONSchemaProps {
optional string id = 1;
optional string schema = 2;
optional string ref = 3;
optional string description = 4;
optional string type = 5;
optional string format = 6;
optional string title = 7;
optional JSON default = 8;
optional double maximum = 9;
optional bool exclusiveMaximum = 10;
optional double minimum = 11;
optional bool exclusiveMinimum = 12;
optional int64 maxLength = 13;
optional int64 minLength = 14;
optional string pattern = 15;
optional int64 maxItems = 16;
optional int64 minItems = 17;
optional bool uniqueItems = 18;
optional double multipleOf = 19;
repeated JSON enum = 20;
optional int64 maxProperties = 21;
optional int64 minProperties = 22;
repeated string required = 23;
optional JSONSchemaPropsOrArray items = 24;
repeated JSONSchemaProps allOf = 25;
repeated JSONSchemaProps oneOf = 26;
repeated JSONSchemaProps anyOf = 27;
optional JSONSchemaProps not = 28;
map<string, JSONSchemaProps> properties = 29;
optional JSONSchemaPropsOrBool additionalProperties = 30;
map<string, JSONSchemaProps> patternProperties = 31;
map<string, JSONSchemaPropsOrStringArray> dependencies = 32;
optional JSONSchemaPropsOrBool additionalItems = 33;
map<string, JSONSchemaProps> definitions = 34;
optional ExternalDocumentation externalDocs = 35;
optional JSON example = 36;
}
// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps
// or an array of JSONSchemaProps. Mainly here for serialization purposes.
message JSONSchemaPropsOrArray {
optional JSONSchemaProps schema = 1;
repeated JSONSchemaProps jSONSchemas = 2;
}
// JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value.
// Defaults to true for the boolean property.
message JSONSchemaPropsOrBool {
optional bool allows = 1;
optional JSONSchemaProps schema = 2;
}
// JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array.
message JSONSchemaPropsOrStringArray {
optional JSONSchemaProps schema = 1;
repeated string property = 2;
}

View File

@ -0,0 +1,135 @@
/*
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 (
"errors"
"k8s.io/apimachinery/pkg/util/json"
)
var jsTrue = []byte("true")
var jsFalse = []byte("false")
func (s JSONSchemaPropsOrBool) MarshalJSON() ([]byte, error) {
if s.Schema != nil {
return json.Marshal(s.Schema)
}
if s.Schema == nil && !s.Allows {
return jsFalse, nil
}
return jsTrue, nil
}
func (s *JSONSchemaPropsOrBool) UnmarshalJSON(data []byte) error {
var nw JSONSchemaPropsOrBool
switch {
case len(data) == 0:
case data[0] == '{':
var sch JSONSchemaProps
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Allows = true
nw.Schema = &sch
case len(data) == 4 && string(data) == "true":
nw.Allows = true
case len(data) == 5 && string(data) == "false":
nw.Allows = false
default:
return errors.New("boolean or JSON schema expected")
}
*s = nw
return nil
}
func (s JSONSchemaPropsOrStringArray) MarshalJSON() ([]byte, error) {
if len(s.Property) > 0 {
return json.Marshal(s.Property)
}
if s.Schema != nil {
return json.Marshal(s.Schema)
}
return []byte("null"), nil
}
func (s *JSONSchemaPropsOrStringArray) UnmarshalJSON(data []byte) error {
var first byte
if len(data) > 1 {
first = data[0]
}
var nw JSONSchemaPropsOrStringArray
if first == '{' {
var sch JSONSchemaProps
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Schema = &sch
}
if first == '[' {
if err := json.Unmarshal(data, &nw.Property); err != nil {
return err
}
}
*s = nw
return nil
}
func (s JSONSchemaPropsOrArray) MarshalJSON() ([]byte, error) {
if len(s.JSONSchemas) > 0 {
return json.Marshal(s.JSONSchemas)
}
return json.Marshal(s.Schema)
}
func (s *JSONSchemaPropsOrArray) UnmarshalJSON(data []byte) error {
var nw JSONSchemaPropsOrArray
var first byte
if len(data) > 1 {
first = data[0]
}
if first == '{' {
var sch JSONSchemaProps
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Schema = &sch
}
if first == '[' {
if err := json.Unmarshal(data, &nw.JSONSchemas); err != nil {
return err
}
}
*s = nw
return nil
}
func (s JSON) MarshalJSON() ([]byte, error) {
if len(s.Raw) > 0 {
return s.Raw, nil
}
return []byte("null"), nil
}
func (s *JSON) UnmarshalJSON(data []byte) error {
if len(data) > 0 && string(data) != "null" {
s.Raw = data
}
return nil
}

View File

@ -0,0 +1,150 @@
/*
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 (
"encoding/json"
"reflect"
"testing"
)
type JSONSchemaPropsOrBoolHolder struct {
JSPoB JSONSchemaPropsOrBool `json:"val1"`
JSPoBOmitEmpty *JSONSchemaPropsOrBool `json:"val2,omitempty"`
}
func TestJSONSchemaPropsOrBoolUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result JSONSchemaPropsOrBoolHolder
}{
{`{}`, JSONSchemaPropsOrBoolHolder{}},
{`{"val1": {}}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Allows: true, Schema: &JSONSchemaProps{}}}},
{`{"val1": {"type":"string"}}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Allows: true, Schema: &JSONSchemaProps{Type: "string"}}}},
{`{"val1": false}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{}}},
{`{"val1": true}`, JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Allows: true}}},
{`{"val2": {}}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Allows: true, Schema: &JSONSchemaProps{}}}},
{`{"val2": {"type":"string"}}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Allows: true, Schema: &JSONSchemaProps{Type: "string"}}}},
{`{"val2": false}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{}}},
{`{"val2": true}`, JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Allows: true}}},
}
for _, c := range cases {
var result JSONSchemaPropsOrBoolHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result, c.result) {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}
func TestStringArrayOrStringMarshalJSON(t *testing.T) {
cases := []struct {
input JSONSchemaPropsOrBoolHolder
result string
}{
{JSONSchemaPropsOrBoolHolder{}, `{"val1":false}`},
{JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{}}}, `{"val1":{}}`},
{JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":{"type":"string"}}`},
{JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{}}, `{"val1":false}`},
{JSONSchemaPropsOrBoolHolder{JSPoB: JSONSchemaPropsOrBool{Allows: true}}, `{"val1":true}`},
{JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{}}}, `{"val1":false,"val2":{}}`},
{JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":false,"val2":{"type":"string"}}`},
{JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{}}, `{"val1":false,"val2":false}`},
{JSONSchemaPropsOrBoolHolder{JSPoBOmitEmpty: &JSONSchemaPropsOrBool{Allows: true}}, `{"val1":false,"val2":true}`},
}
for _, c := range cases {
result, err := json.Marshal(&c.input)
if err != nil {
t.Errorf("Unexpected error marshaling input '%v': %v", c.input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input '%v': expected: %q, got %q", c.input, c.result, string(result))
}
}
}
type JSONSchemaPropsOrArrayHolder struct {
JSPoA JSONSchemaPropsOrArray `json:"val1"`
JSPoAOmitEmpty *JSONSchemaPropsOrArray `json:"val2,omitempty"`
}
func TestJSONSchemaPropsOrArrayUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result JSONSchemaPropsOrArrayHolder
}{
{`{}`, JSONSchemaPropsOrArrayHolder{}},
{`{"val1": {}}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}},
{`{"val1": {"type":"string"}}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}},
{`{"val1": [{}]}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}},
{`{"val1": [{},{"type":"string"}]}`, JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}},
{`{"val2": {}}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}},
{`{"val2": {"type":"string"}}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}},
{`{"val2": [{}]}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}},
{`{"val2": [{},{"type":"string"}]}`, JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}},
}
for _, c := range cases {
var result JSONSchemaPropsOrArrayHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result, c.result) {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}
func TestJSONSchemaPropsOrArrayMarshalJSON(t *testing.T) {
cases := []struct {
input JSONSchemaPropsOrArrayHolder
result string
}{
{JSONSchemaPropsOrArrayHolder{}, `{"val1":null}`},
{JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}, `{"val1":{}}`},
{JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":{"type":"string"}}`},
{JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}, `{"val1":[{}]}`},
{JSONSchemaPropsOrArrayHolder{JSPoA: JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}, `{"val1":[{},{"type":"string"}]}`},
{JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{}}, `{"val1":null,"val2":null}`},
{JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{}}}, `{"val1":null,"val2":{}}`},
{JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{Schema: &JSONSchemaProps{Type: "string"}}}, `{"val1":null,"val2":{"type":"string"}}`},
{JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}}}}, `{"val1":null,"val2":[{}]}`},
{JSONSchemaPropsOrArrayHolder{JSPoAOmitEmpty: &JSONSchemaPropsOrArray{JSONSchemas: []JSONSchemaProps{{}, {Type: "string"}}}}, `{"val1":null,"val2":[{},{"type":"string"}]}`},
}
for i, c := range cases {
result, err := json.Marshal(&c.input)
if err != nil {
t.Errorf("%d: Unexpected error marshaling input '%v': %v", i, c.input, err)
}
if string(result) != c.result {
t.Errorf("%d: Failed to marshal input '%v': expected: %q, got %q", i, c.input, c.result, string(result))
}
}
}

View File

@ -0,0 +1,61 @@
/*
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"
)
const GroupName = "apiextensions.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns back a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&CustomResourceDefinition{},
&CustomResourceDefinitionList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
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)
}

View File

@ -0,0 +1,265 @@
/*
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"
)
// CustomResourceDefinitionSpec describes how a user wants their resource to appear
type CustomResourceDefinitionSpec struct {
// Group is the group this resource belongs in
Group string `json:"group" protobuf:"bytes,1,opt,name=group"`
// Version is the version this resource belongs in
// Should be always first item in Versions field if provided.
// Optional, but at least one of Version or Versions must be set.
// Deprecated: Please use `Versions`.
// +optional
Version string `json:"version,omitempty" protobuf:"bytes,2,opt,name=version"`
// Names are the names used to describe this custom resource
Names CustomResourceDefinitionNames `json:"names" protobuf:"bytes,3,opt,name=names"`
// Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced
Scope ResourceScope `json:"scope" protobuf:"bytes,4,opt,name=scope,casttype=ResourceScope"`
// Validation describes the validation methods for CustomResources
// +optional
Validation *CustomResourceValidation `json:"validation,omitempty" protobuf:"bytes,5,opt,name=validation"`
// Subresources describes the subresources for CustomResources
// +optional
Subresources *CustomResourceSubresources `json:"subresources,omitempty" protobuf:"bytes,6,opt,name=subresources"`
// Versions is the list of all supported versions for this resource.
// If Version field is provided, this field is optional.
// Validation: All versions must use the same validation schema for now. i.e., top
// level Validation field is applied to all of these versions.
// Order: The version name will be used to compute the order.
// If the version string is "kube-like", it will sort above non "kube-like" version strings, which are ordered
// lexicographically. "Kube-like" versions start with a "v", then are followed by a number (the major version),
// then optionally the string "alpha" or "beta" and another number (the minor version). These are sorted first
// by GA > beta > alpha (where GA is a version with no suffix such as beta or alpha), and then by comparing
// major version, then minor version. An example sorted list of versions:
// v10, v2, v1, v11beta2, v10beta3, v3beta1, v12alpha1, v11alpha2, foo1, foo10.
// +optional
Versions []CustomResourceDefinitionVersion `json:"versions,omitempty" protobuf:"bytes,7,rep,name=versions"`
// AdditionalPrinterColumns are additional columns shown e.g. in kubectl next to the name. Defaults to a created-at column.
// +optional
AdditionalPrinterColumns []CustomResourceColumnDefinition `json:"additionalPrinterColumns,omitempty" protobuf:"bytes,8,rep,name=additionalPrinterColumns"`
}
type CustomResourceDefinitionVersion struct {
// Name is the version name, e.g. “v1”, “v2beta1”, etc.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// Served is a flag enabling/disabling this version from being served via REST APIs
Served bool `json:"served" protobuf:"varint,2,opt,name=served"`
// Storage flags the version as storage version. There must be exactly one
// flagged as storage version.
Storage bool `json:"storage" protobuf:"varint,3,opt,name=storage"`
}
// CustomResourceColumnDefinition specifies a column for server side printing.
type CustomResourceColumnDefinition struct {
// name is a human readable name for the column.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// type is an OpenAPI type definition for this column.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.
Type string `json:"type" protobuf:"bytes,2,opt,name=type"`
// format is an optional OpenAPI type definition for this column. The 'name' format is applied
// to the primary identifier column to assist in clients identifying column is the resource name.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.
// +optional
Format string `json:"format,omitempty" protobuf:"bytes,3,opt,name=format"`
// description is a human readable description of this column.
// +optional
Description string `json:"description,omitempty" protobuf:"bytes,4,opt,name=description"`
// priority is an integer defining the relative importance of this column compared to others. Lower
// numbers are considered higher priority. Columns that may be omitted in limited space scenarios
// should be given a higher priority.
// +optional
Priority int32 `json:"priority,omitempty" protobuf:"bytes,5,opt,name=priority"`
// JSONPath is a simple JSON path, i.e. with array notation.
JSONPath string `json:"JSONPath" protobuf:"bytes,6,opt,name=JSONPath"`
}
// CustomResourceDefinitionNames indicates the names to serve this CustomResourceDefinition
type CustomResourceDefinitionNames struct {
// Plural is the plural name of the resource to serve. It must match the name of the CustomResourceDefinition-registration
// too: plural.group and it must be all lowercase.
Plural string `json:"plural" protobuf:"bytes,1,opt,name=plural"`
// Singular is the singular name of the resource. It must be all lowercase Defaults to lowercased <kind>
// +optional
Singular string `json:"singular,omitempty" protobuf:"bytes,2,opt,name=singular"`
// ShortNames are short names for the resource. It must be all lowercase.
// +optional
ShortNames []string `json:"shortNames,omitempty" protobuf:"bytes,3,opt,name=shortNames"`
// Kind is the serialized kind of the resource. It is normally CamelCase and singular.
Kind string `json:"kind" protobuf:"bytes,4,opt,name=kind"`
// ListKind is the serialized kind of the list for this resource. Defaults to <kind>List.
// +optional
ListKind string `json:"listKind,omitempty" protobuf:"bytes,5,opt,name=listKind"`
// Categories is a list of grouped resources custom resources belong to (e.g. 'all')
// +optional
Categories []string `json:"categories,omitempty" protobuf:"bytes,6,rep,name=categories"`
}
// ResourceScope is an enum defining the different scopes available to a custom resource
type ResourceScope string
const (
ClusterScoped ResourceScope = "Cluster"
NamespaceScoped ResourceScope = "Namespaced"
)
type ConditionStatus string
// These are valid condition statuses. "ConditionTrue" means a resource is in the condition.
// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes
// can't decide if a resource is in the condition or not. In the future, we could add other
// intermediate conditions, e.g. ConditionDegraded.
const (
ConditionTrue ConditionStatus = "True"
ConditionFalse ConditionStatus = "False"
ConditionUnknown ConditionStatus = "Unknown"
)
// CustomResourceDefinitionConditionType is a valid value for CustomResourceDefinitionCondition.Type
type CustomResourceDefinitionConditionType string
const (
// Established means that the resource has become active. A resource is established when all names are
// accepted without a conflict for the first time. A resource stays established until deleted, even during
// a later NamesAccepted due to changed names. Note that not all names can be changed.
Established CustomResourceDefinitionConditionType = "Established"
// NamesAccepted means the names chosen for this CustomResourceDefinition do not conflict with others in
// the group and are therefore accepted.
NamesAccepted CustomResourceDefinitionConditionType = "NamesAccepted"
// Terminating means that the CustomResourceDefinition has been deleted and is cleaning up.
Terminating CustomResourceDefinitionConditionType = "Terminating"
)
// CustomResourceDefinitionCondition contains details for the current condition of this pod.
type CustomResourceDefinitionCondition struct {
// Type is the type of the condition.
Type CustomResourceDefinitionConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=CustomResourceDefinitionConditionType"`
// Status is the status of the condition.
// Can be True, False, Unknown.
Status ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=ConditionStatus"`
// Last time the condition transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,3,opt,name=lastTransitionTime"`
// Unique, one-word, CamelCase reason for the condition's last transition.
// +optional
Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"`
// Human-readable message indicating details about last transition.
// +optional
Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"`
}
// CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition
type CustomResourceDefinitionStatus struct {
// Conditions indicate state for particular aspects of a CustomResourceDefinition
Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"`
// AcceptedNames are the names that are actually being used to serve discovery
// They may be different than the names in spec.
AcceptedNames CustomResourceDefinitionNames `json:"acceptedNames" protobuf:"bytes,2,opt,name=acceptedNames"`
// StoredVersions are all versions of CustomResources that were ever persisted. Tracking these
// versions allows a migration path for stored versions in etcd. The field is mutable
// so the migration controller can first finish a migration to another version (i.e.
// that no old objects are left in the storage), and then remove the rest of the
// versions from this list.
// None of the versions in this list can be removed from the spec.Versions field.
StoredVersions []string `json:"storedVersions" protobuf:"bytes,3,rep,name=storedVersions"`
}
// CustomResourceCleanupFinalizer is the name of the finalizer which will delete instances of
// a CustomResourceDefinition
const CustomResourceCleanupFinalizer = "customresourcecleanup.apiextensions.k8s.io"
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CustomResourceDefinition represents a resource that should be exposed on the API server. Its name MUST be in the format
// <.spec.name>.<.spec.group>.
type CustomResourceDefinition struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Spec describes how the user wants the resources to appear
Spec CustomResourceDefinitionSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
// Status indicates the actual state of the CustomResourceDefinition
// +optional
Status CustomResourceDefinitionStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// CustomResourceDefinitionList is a list of CustomResourceDefinition objects.
type CustomResourceDefinitionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Items individual CustomResourceDefinitions
Items []CustomResourceDefinition `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// CustomResourceValidation is a list of validation methods for CustomResources.
type CustomResourceValidation struct {
// OpenAPIV3Schema is the OpenAPI v3 schema to be validated against.
// +optional
OpenAPIV3Schema *JSONSchemaProps `json:"openAPIV3Schema,omitempty" protobuf:"bytes,1,opt,name=openAPIV3Schema"`
}
// CustomResourceSubresources defines the status and scale subresources for CustomResources.
type CustomResourceSubresources struct {
// Status denotes the status subresource for CustomResources
// +optional
Status *CustomResourceSubresourceStatus `json:"status,omitempty" protobuf:"bytes,1,opt,name=status"`
// Scale denotes the scale subresource for CustomResources
// +optional
Scale *CustomResourceSubresourceScale `json:"scale,omitempty" protobuf:"bytes,2,opt,name=scale"`
}
// CustomResourceSubresourceStatus defines how to serve the status subresource for CustomResources.
// Status is represented by the `.status` JSON path inside of a CustomResource. When set,
// * exposes a /status subresource for the custom resource
// * PUT requests to the /status subresource take a custom resource object, and ignore changes to anything except the status stanza
// * PUT/POST/PATCH requests to the custom resource ignore changes to the status stanza
type CustomResourceSubresourceStatus struct{}
// CustomResourceSubresourceScale defines how to serve the scale subresource for CustomResources.
type CustomResourceSubresourceScale struct {
// SpecReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Spec.Replicas.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .spec.
// If there is no value under the given path in the CustomResource, the /scale subresource will return an error on GET.
SpecReplicasPath string `json:"specReplicasPath" protobuf:"bytes,1,name=specReplicasPath"`
// StatusReplicasPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Replicas.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .status.
// If there is no value under the given path in the CustomResource, the status replica value in the /scale subresource
// will default to 0.
StatusReplicasPath string `json:"statusReplicasPath" protobuf:"bytes,2,opt,name=statusReplicasPath"`
// LabelSelectorPath defines the JSON path inside of a CustomResource that corresponds to Scale.Status.Selector.
// Only JSON paths without the array notation are allowed.
// Must be a JSON Path under .status.
// Must be set to work with HPA.
// If there is no value under the given path in the CustomResource, the status label selector value in the /scale
// subresource will default to the empty string.
// +optional
LabelSelectorPath *string `json:"labelSelectorPath,omitempty" protobuf:"bytes,3,opt,name=labelSelectorPath"`
}

View File

@ -0,0 +1,150 @@
/*
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
// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).
type JSONSchemaProps struct {
ID string `json:"id,omitempty" protobuf:"bytes,1,opt,name=id"`
Schema JSONSchemaURL `json:"$schema,omitempty" protobuf:"bytes,2,opt,name=schema"`
Ref *string `json:"$ref,omitempty" protobuf:"bytes,3,opt,name=ref"`
Description string `json:"description,omitempty" protobuf:"bytes,4,opt,name=description"`
Type string `json:"type,omitempty" protobuf:"bytes,5,opt,name=type"`
Format string `json:"format,omitempty" protobuf:"bytes,6,opt,name=format"`
Title string `json:"title,omitempty" protobuf:"bytes,7,opt,name=title"`
Default *JSON `json:"default,omitempty" protobuf:"bytes,8,opt,name=default"`
Maximum *float64 `json:"maximum,omitempty" protobuf:"bytes,9,opt,name=maximum"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty" protobuf:"bytes,10,opt,name=exclusiveMaximum"`
Minimum *float64 `json:"minimum,omitempty" protobuf:"bytes,11,opt,name=minimum"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty" protobuf:"bytes,12,opt,name=exclusiveMinimum"`
MaxLength *int64 `json:"maxLength,omitempty" protobuf:"bytes,13,opt,name=maxLength"`
MinLength *int64 `json:"minLength,omitempty" protobuf:"bytes,14,opt,name=minLength"`
Pattern string `json:"pattern,omitempty" protobuf:"bytes,15,opt,name=pattern"`
MaxItems *int64 `json:"maxItems,omitempty" protobuf:"bytes,16,opt,name=maxItems"`
MinItems *int64 `json:"minItems,omitempty" protobuf:"bytes,17,opt,name=minItems"`
UniqueItems bool `json:"uniqueItems,omitempty" protobuf:"bytes,18,opt,name=uniqueItems"`
MultipleOf *float64 `json:"multipleOf,omitempty" protobuf:"bytes,19,opt,name=multipleOf"`
Enum []JSON `json:"enum,omitempty" protobuf:"bytes,20,rep,name=enum"`
MaxProperties *int64 `json:"maxProperties,omitempty" protobuf:"bytes,21,opt,name=maxProperties"`
MinProperties *int64 `json:"minProperties,omitempty" protobuf:"bytes,22,opt,name=minProperties"`
Required []string `json:"required,omitempty" protobuf:"bytes,23,rep,name=required"`
Items *JSONSchemaPropsOrArray `json:"items,omitempty" protobuf:"bytes,24,opt,name=items"`
AllOf []JSONSchemaProps `json:"allOf,omitempty" protobuf:"bytes,25,rep,name=allOf"`
OneOf []JSONSchemaProps `json:"oneOf,omitempty" protobuf:"bytes,26,rep,name=oneOf"`
AnyOf []JSONSchemaProps `json:"anyOf,omitempty" protobuf:"bytes,27,rep,name=anyOf"`
Not *JSONSchemaProps `json:"not,omitempty" protobuf:"bytes,28,opt,name=not"`
Properties map[string]JSONSchemaProps `json:"properties,omitempty" protobuf:"bytes,29,rep,name=properties"`
AdditionalProperties *JSONSchemaPropsOrBool `json:"additionalProperties,omitempty" protobuf:"bytes,30,opt,name=additionalProperties"`
PatternProperties map[string]JSONSchemaProps `json:"patternProperties,omitempty" protobuf:"bytes,31,rep,name=patternProperties"`
Dependencies JSONSchemaDependencies `json:"dependencies,omitempty" protobuf:"bytes,32,opt,name=dependencies"`
AdditionalItems *JSONSchemaPropsOrBool `json:"additionalItems,omitempty" protobuf:"bytes,33,opt,name=additionalItems"`
Definitions JSONSchemaDefinitions `json:"definitions,omitempty" protobuf:"bytes,34,opt,name=definitions"`
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" protobuf:"bytes,35,opt,name=externalDocs"`
Example *JSON `json:"example,omitempty" protobuf:"bytes,36,opt,name=example"`
}
// JSON represents any valid JSON value.
// These types are supported: bool, int64, float64, string, []interface{}, map[string]interface{} and nil.
type JSON struct {
Raw []byte `protobuf:"bytes,1,opt,name=raw"`
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSON) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported
return []string{}
}
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
func (_ JSON) OpenAPISchemaFormat() string { return "" }
// JSONSchemaURL represents a schema url.
type JSONSchemaURL string
// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps
// or an array of JSONSchemaProps. Mainly here for serialization purposes.
type JSONSchemaPropsOrArray struct {
Schema *JSONSchemaProps `protobuf:"bytes,1,opt,name=schema"`
JSONSchemas []JSONSchemaProps `protobuf:"bytes,2,rep,name=jSONSchemas"`
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSONSchemaPropsOrArray) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported
return []string{}
}
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
func (_ JSONSchemaPropsOrArray) OpenAPISchemaFormat() string { return "" }
// JSONSchemaPropsOrBool represents JSONSchemaProps or a boolean value.
// Defaults to true for the boolean property.
type JSONSchemaPropsOrBool struct {
Allows bool `protobuf:"varint,1,opt,name=allows"`
Schema *JSONSchemaProps `protobuf:"bytes,2,opt,name=schema"`
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSONSchemaPropsOrBool) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported
return []string{}
}
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
func (_ JSONSchemaPropsOrBool) OpenAPISchemaFormat() string { return "" }
// JSONSchemaDependencies represent a dependencies property.
type JSONSchemaDependencies map[string]JSONSchemaPropsOrStringArray
// JSONSchemaPropsOrStringArray represents a JSONSchemaProps or a string array.
type JSONSchemaPropsOrStringArray struct {
Schema *JSONSchemaProps `protobuf:"bytes,1,opt,name=schema"`
Property []string `protobuf:"bytes,2,rep,name=property"`
}
// OpenAPISchemaType is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
//
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSONSchemaPropsOrStringArray) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported
return []string{}
}
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
// the OpenAPI spec of this type.
func (_ JSONSchemaPropsOrStringArray) OpenAPISchemaFormat() string { return "" }
// JSONSchemaDefinitions contains the models explicitly defined in this spec.
type JSONSchemaDefinitions map[string]JSONSchemaProps
// ExternalDocumentation allows referencing an external resource for extended documentation.
type ExternalDocumentation struct {
Description string `json:"description,omitempty" protobuf:"bytes,1,opt,name=description"`
URL string `json:"url,omitempty" protobuf:"bytes,2,opt,name=url"`
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,470 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceColumnDefinition) DeepCopyInto(out *CustomResourceColumnDefinition) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceColumnDefinition.
func (in *CustomResourceColumnDefinition) DeepCopy() *CustomResourceColumnDefinition {
if in == nil {
return nil
}
out := new(CustomResourceColumnDefinition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinition) DeepCopyInto(out *CustomResourceDefinition) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinition.
func (in *CustomResourceDefinition) DeepCopy() *CustomResourceDefinition {
if in == nil {
return nil
}
out := new(CustomResourceDefinition)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomResourceDefinition) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinitionCondition) DeepCopyInto(out *CustomResourceDefinitionCondition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinitionCondition.
func (in *CustomResourceDefinitionCondition) DeepCopy() *CustomResourceDefinitionCondition {
if in == nil {
return nil
}
out := new(CustomResourceDefinitionCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinitionList) DeepCopyInto(out *CustomResourceDefinitionList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomResourceDefinition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinitionList.
func (in *CustomResourceDefinitionList) DeepCopy() *CustomResourceDefinitionList {
if in == nil {
return nil
}
out := new(CustomResourceDefinitionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomResourceDefinitionList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinitionNames) DeepCopyInto(out *CustomResourceDefinitionNames) {
*out = *in
if in.ShortNames != nil {
in, out := &in.ShortNames, &out.ShortNames
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Categories != nil {
in, out := &in.Categories, &out.Categories
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinitionNames.
func (in *CustomResourceDefinitionNames) DeepCopy() *CustomResourceDefinitionNames {
if in == nil {
return nil
}
out := new(CustomResourceDefinitionNames)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefinitionSpec) {
*out = *in
in.Names.DeepCopyInto(&out.Names)
if in.Validation != nil {
in, out := &in.Validation, &out.Validation
*out = new(CustomResourceValidation)
(*in).DeepCopyInto(*out)
}
if in.Subresources != nil {
in, out := &in.Subresources, &out.Subresources
*out = new(CustomResourceSubresources)
(*in).DeepCopyInto(*out)
}
if in.Versions != nil {
in, out := &in.Versions, &out.Versions
*out = make([]CustomResourceDefinitionVersion, len(*in))
copy(*out, *in)
}
if in.AdditionalPrinterColumns != nil {
in, out := &in.AdditionalPrinterColumns, &out.AdditionalPrinterColumns
*out = make([]CustomResourceColumnDefinition, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinitionSpec.
func (in *CustomResourceDefinitionSpec) DeepCopy() *CustomResourceDefinitionSpec {
if in == nil {
return nil
}
out := new(CustomResourceDefinitionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinitionStatus) DeepCopyInto(out *CustomResourceDefinitionStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]CustomResourceDefinitionCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.AcceptedNames.DeepCopyInto(&out.AcceptedNames)
if in.StoredVersions != nil {
in, out := &in.StoredVersions, &out.StoredVersions
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinitionStatus.
func (in *CustomResourceDefinitionStatus) DeepCopy() *CustomResourceDefinitionStatus {
if in == nil {
return nil
}
out := new(CustomResourceDefinitionStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceDefinitionVersion) DeepCopyInto(out *CustomResourceDefinitionVersion) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceDefinitionVersion.
func (in *CustomResourceDefinitionVersion) DeepCopy() *CustomResourceDefinitionVersion {
if in == nil {
return nil
}
out := new(CustomResourceDefinitionVersion)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceSubresourceScale) DeepCopyInto(out *CustomResourceSubresourceScale) {
*out = *in
if in.LabelSelectorPath != nil {
in, out := &in.LabelSelectorPath, &out.LabelSelectorPath
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceSubresourceScale.
func (in *CustomResourceSubresourceScale) DeepCopy() *CustomResourceSubresourceScale {
if in == nil {
return nil
}
out := new(CustomResourceSubresourceScale)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceSubresourceStatus) DeepCopyInto(out *CustomResourceSubresourceStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceSubresourceStatus.
func (in *CustomResourceSubresourceStatus) DeepCopy() *CustomResourceSubresourceStatus {
if in == nil {
return nil
}
out := new(CustomResourceSubresourceStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceSubresources) DeepCopyInto(out *CustomResourceSubresources) {
*out = *in
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = new(CustomResourceSubresourceStatus)
**out = **in
}
if in.Scale != nil {
in, out := &in.Scale, &out.Scale
*out = new(CustomResourceSubresourceScale)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceSubresources.
func (in *CustomResourceSubresources) DeepCopy() *CustomResourceSubresources {
if in == nil {
return nil
}
out := new(CustomResourceSubresources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomResourceValidation) DeepCopyInto(out *CustomResourceValidation) {
*out = *in
if in.OpenAPIV3Schema != nil {
in, out := &in.OpenAPIV3Schema, &out.OpenAPIV3Schema
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResourceValidation.
func (in *CustomResourceValidation) DeepCopy() *CustomResourceValidation {
if in == nil {
return nil
}
out := new(CustomResourceValidation)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExternalDocumentation) DeepCopyInto(out *ExternalDocumentation) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalDocumentation.
func (in *ExternalDocumentation) DeepCopy() *ExternalDocumentation {
if in == nil {
return nil
}
out := new(ExternalDocumentation)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JSON) DeepCopyInto(out *JSON) {
*out = *in
if in.Raw != nil {
in, out := &in.Raw, &out.Raw
*out = make([]byte, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSON.
func (in *JSON) DeepCopy() *JSON {
if in == nil {
return nil
}
out := new(JSON)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in JSONSchemaDefinitions) DeepCopyInto(out *JSONSchemaDefinitions) {
{
in := &in
*out = make(JSONSchemaDefinitions, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONSchemaDefinitions.
func (in JSONSchemaDefinitions) DeepCopy() JSONSchemaDefinitions {
if in == nil {
return nil
}
out := new(JSONSchemaDefinitions)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in JSONSchemaDependencies) DeepCopyInto(out *JSONSchemaDependencies) {
{
in := &in
*out = make(JSONSchemaDependencies, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONSchemaDependencies.
func (in JSONSchemaDependencies) DeepCopy() JSONSchemaDependencies {
if in == nil {
return nil
}
out := new(JSONSchemaDependencies)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JSONSchemaProps) DeepCopyInto(out *JSONSchemaProps) {
clone := in.DeepCopy()
*out = *clone
return
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JSONSchemaPropsOrArray) DeepCopyInto(out *JSONSchemaPropsOrArray) {
*out = *in
if in.Schema != nil {
in, out := &in.Schema, &out.Schema
*out = (*in).DeepCopy()
}
if in.JSONSchemas != nil {
in, out := &in.JSONSchemas, &out.JSONSchemas
*out = make([]JSONSchemaProps, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONSchemaPropsOrArray.
func (in *JSONSchemaPropsOrArray) DeepCopy() *JSONSchemaPropsOrArray {
if in == nil {
return nil
}
out := new(JSONSchemaPropsOrArray)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JSONSchemaPropsOrBool) DeepCopyInto(out *JSONSchemaPropsOrBool) {
*out = *in
if in.Schema != nil {
in, out := &in.Schema, &out.Schema
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONSchemaPropsOrBool.
func (in *JSONSchemaPropsOrBool) DeepCopy() *JSONSchemaPropsOrBool {
if in == nil {
return nil
}
out := new(JSONSchemaPropsOrBool)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JSONSchemaPropsOrStringArray) DeepCopyInto(out *JSONSchemaPropsOrStringArray) {
*out = *in
if in.Schema != nil {
in, out := &in.Schema, &out.Schema
*out = (*in).DeepCopy()
}
if in.Property != nil {
in, out := &in.Property, &out.Property
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONSchemaPropsOrStringArray.
func (in *JSONSchemaPropsOrStringArray) DeepCopy() *JSONSchemaPropsOrStringArray {
if in == nil {
return nil
}
out := new(JSONSchemaPropsOrStringArray)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,48 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by defaulter-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&CustomResourceDefinition{}, func(obj interface{}) { SetObjectDefaults_CustomResourceDefinition(obj.(*CustomResourceDefinition)) })
scheme.AddTypeDefaultingFunc(&CustomResourceDefinitionList{}, func(obj interface{}) {
SetObjectDefaults_CustomResourceDefinitionList(obj.(*CustomResourceDefinitionList))
})
return nil
}
func SetObjectDefaults_CustomResourceDefinition(in *CustomResourceDefinition) {
SetDefaults_CustomResourceDefinition(in)
SetDefaults_CustomResourceDefinitionSpec(&in.Spec)
}
func SetObjectDefaults_CustomResourceDefinitionList(in *CustomResourceDefinitionList) {
for i := range in.Items {
a := &in.Items[i]
SetObjectDefaults_CustomResourceDefinition(a)
}
}

View File

@ -0,0 +1,546 @@
/*
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 validation
import (
"fmt"
"reflect"
"strings"
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/sets"
validationutil "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
)
var (
printerColumnDatatypes = sets.NewString("integer", "number", "string", "boolean", "date")
customResourceColumnDefinitionFormats = sets.NewString("int32", "int64", "float", "double", "byte", "date", "date-time", "password")
)
// ValidateCustomResourceDefinition statically validates
func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinition) field.ErrorList {
nameValidationFn := func(name string, prefix bool) []string {
ret := genericvalidation.NameIsDNSSubdomain(name, prefix)
requiredName := obj.Spec.Names.Plural + "." + obj.Spec.Group
if name != requiredName {
ret = append(ret, fmt.Sprintf(`must be spec.names.plural+"."+spec.group`))
}
return ret
}
allErrs := genericvalidation.ValidateObjectMeta(&obj.ObjectMeta, false, nameValidationFn, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateCustomResourceDefinitionSpec(&obj.Spec, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
return allErrs
}
// ValidateCustomResourceDefinitionUpdate statically validates
func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition) field.ErrorList {
allErrs := genericvalidation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateCustomResourceDefinitionSpecUpdate(&obj.Spec, &oldObj.Spec, apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established), field.NewPath("spec"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
return allErrs
}
// ValidateCustomResourceDefinitionStoredVersions statically validates
func ValidateCustomResourceDefinitionStoredVersions(storedVersions []string, versions []apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path) field.ErrorList {
if len(storedVersions) == 0 {
return field.ErrorList{field.Invalid(fldPath, storedVersions, "must have at least one stored version")}
}
allErrs := field.ErrorList{}
storedVersionsMap := map[string]int{}
for i, v := range storedVersions {
storedVersionsMap[v] = i
}
for _, v := range versions {
_, ok := storedVersionsMap[v.Name]
if v.Storage && !ok {
allErrs = append(allErrs, field.Invalid(fldPath, v, "must have the storage version "+v.Name))
}
if ok {
delete(storedVersionsMap, v.Name)
}
}
for v, i := range storedVersionsMap {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), v, "must appear in spec.versions"))
}
return allErrs
}
// ValidateUpdateCustomResourceDefinitionStatus statically validates
func ValidateUpdateCustomResourceDefinitionStatus(obj, oldObj *apiextensions.CustomResourceDefinition) field.ErrorList {
allErrs := genericvalidation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
return allErrs
}
// ValidateCustomResourceDefinitionSpec statically validates
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(spec.Group) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("group"), ""))
} else if errs := validationutil.IsDNS1123Subdomain(spec.Group); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("group"), spec.Group, strings.Join(errs, ",")))
} else if len(strings.Split(spec.Group, ".")) < 2 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("group"), spec.Group, "should be a domain with at least one dot"))
}
switch spec.Scope {
case "":
allErrs = append(allErrs, field.Required(fldPath.Child("scope"), ""))
case apiextensions.ClusterScoped, apiextensions.NamespaceScoped:
default:
allErrs = append(allErrs, field.NotSupported(fldPath.Child("scope"), spec.Scope, []string{string(apiextensions.ClusterScoped), string(apiextensions.NamespaceScoped)}))
}
storageFlagCount := 0
versionsMap := map[string]bool{}
uniqueNames := true
for i, version := range spec.Versions {
if version.Storage {
storageFlagCount++
}
if versionsMap[version.Name] {
uniqueNames = false
} else {
versionsMap[version.Name] = true
}
if errs := validationutil.IsDNS1035Label(version.Name); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
}
}
if !uniqueNames {
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "must contain unique version names"))
}
if storageFlagCount != 1 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "must have exactly one version marked as storage version"))
}
if len(spec.Version) != 0 {
if errs := validationutil.IsDNS1035Label(spec.Version); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), spec.Version, strings.Join(errs, ",")))
}
if len(spec.Versions) >= 1 && spec.Versions[0].Name != spec.Version {
allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), spec.Version, "must match the first version in spec.versions"))
}
}
// in addition to the basic name restrictions, some names are required for spec, but not for status
if len(spec.Names.Plural) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("names", "plural"), ""))
}
if len(spec.Names.Singular) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("names", "singular"), ""))
}
if len(spec.Names.Kind) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("names", "kind"), ""))
}
if len(spec.Names.ListKind) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("names", "listKind"), ""))
}
allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&spec.Names, fldPath.Child("names"))...)
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) {
statusEnabled := false
if spec.Subresources != nil && spec.Subresources.Status != nil {
statusEnabled = true
}
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(spec.Validation, statusEnabled, fldPath.Child("validation"))...)
} else if spec.Validation != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("validation"), "disabled by feature-gate CustomResourceValidation"))
}
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(spec.Subresources, fldPath.Child("subresources"))...)
} else if spec.Subresources != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("subresources"), "disabled by feature-gate CustomResourceSubresources"))
}
for i := range spec.AdditionalPrinterColumns {
if errs := ValidateCustomResourceColumnDefinition(&spec.AdditionalPrinterColumns[i], fldPath.Child("columns").Index(i)); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}
}
return allErrs
}
// ValidateCustomResourceDefinitionSpecUpdate statically validates
func ValidateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.CustomResourceDefinitionSpec, established bool, fldPath *field.Path) field.ErrorList {
allErrs := ValidateCustomResourceDefinitionSpec(spec, fldPath)
if established {
// these effect the storage and cannot be changed therefore
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Scope, oldSpec.Scope, fldPath.Child("scope"))...)
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Names.Kind, oldSpec.Names.Kind, fldPath.Child("names", "kind"))...)
}
// these affects the resource name, which is always immutable, so this can't be updated.
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Group, oldSpec.Group, fldPath.Child("group"))...)
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Names.Plural, oldSpec.Names.Plural, fldPath.Child("names", "plural"))...)
return allErrs
}
// ValidateCustomResourceDefinitionStatus statically validates
func ValidateCustomResourceDefinitionStatus(status *apiextensions.CustomResourceDefinitionStatus, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&status.AcceptedNames, fldPath.Child("acceptedNames"))...)
return allErrs
}
// ValidateCustomResourceDefinitionNames statically validates
func ValidateCustomResourceDefinitionNames(names *apiextensions.CustomResourceDefinitionNames, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if errs := validationutil.IsDNS1035Label(names.Plural); len(names.Plural) > 0 && len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("plural"), names.Plural, strings.Join(errs, ",")))
}
if errs := validationutil.IsDNS1035Label(names.Singular); len(names.Singular) > 0 && len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("singular"), names.Singular, strings.Join(errs, ",")))
}
if errs := validationutil.IsDNS1035Label(strings.ToLower(names.Kind)); len(names.Kind) > 0 && len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), names.Kind, "may have mixed case, but should otherwise match: "+strings.Join(errs, ",")))
}
if errs := validationutil.IsDNS1035Label(strings.ToLower(names.ListKind)); len(names.ListKind) > 0 && len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("listKind"), names.ListKind, "may have mixed case, but should otherwise match: "+strings.Join(errs, ",")))
}
for i, shortName := range names.ShortNames {
if errs := validationutil.IsDNS1035Label(shortName); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("shortNames").Index(i), shortName, strings.Join(errs, ",")))
}
}
// kind and listKind may not be the same or parsing become ambiguous
if len(names.Kind) > 0 && names.Kind == names.ListKind {
allErrs = append(allErrs, field.Invalid(fldPath.Child("listKind"), names.ListKind, "kind and listKind may not be the same"))
}
for i, category := range names.Categories {
if errs := validationutil.IsDNS1035Label(category); len(errs) > 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("categories").Index(i), category, strings.Join(errs, ",")))
}
}
return allErrs
}
// ValidateCustomResourceColumnDefinition statically validates a printer column.
func ValidateCustomResourceColumnDefinition(col *apiextensions.CustomResourceColumnDefinition, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(col.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
}
if len(col.Type) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("type"), fmt.Sprintf("must be one of %s", strings.Join(printerColumnDatatypes.List(), ","))))
} else if !printerColumnDatatypes.Has(col.Type) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), col.Type, fmt.Sprintf("must be one of %s", strings.Join(printerColumnDatatypes.List(), ","))))
}
if len(col.Format) > 0 && !customResourceColumnDefinitionFormats.Has(col.Format) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("format"), col.Format, fmt.Sprintf("must be one of %s", strings.Join(customResourceColumnDefinitionFormats.List(), ","))))
}
if len(col.JSONPath) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("JSONPath"), ""))
} else if errs := validateSimpleJSONPath(col.JSONPath, fldPath.Child("JSONPath")); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}
return allErrs
}
// specStandardValidator applies validations for different OpenAPI specification versions.
type specStandardValidator interface {
validate(spec *apiextensions.JSONSchemaProps, fldPath *field.Path) field.ErrorList
}
// ValidateCustomResourceDefinitionValidation statically validates
func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiextensions.CustomResourceValidation, statusSubresourceEnabled bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if customResourceValidation == nil {
return allErrs
}
if schema := customResourceValidation.OpenAPIV3Schema; schema != nil {
// if the status subresource is enabled, only certain fields are allowed inside the root schema.
// these fields are chosen such that, if status is extracted as properties["status"], it's validation is not lost.
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) && statusSubresourceEnabled {
v := reflect.ValueOf(schema).Elem()
for i := 0; i < v.NumField(); i++ {
// skip zero values
if value := v.Field(i).Interface(); reflect.DeepEqual(value, reflect.Zero(reflect.TypeOf(value)).Interface()) {
continue
}
fieldName := v.Type().Field(i).Name
// only "object" type is valid at root of the schema since validation schema for status is extracted as properties["status"]
if fieldName == "Type" {
if schema.Type != "object" {
allErrs = append(allErrs, field.Invalid(fldPath.Child("openAPIV3Schema.type"), schema.Type, fmt.Sprintf(`only "object" is allowed as the type at the root of the schema if the status subresource is enabled`)))
break
}
continue
}
if !allowedAtRootSchema(fieldName) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("openAPIV3Schema"), *schema, fmt.Sprintf(`only %v fields are allowed at the root of the schema if the status subresource is enabled`, allowedFieldsAtRootSchema)))
break
}
}
}
openAPIV3Schema := &specStandardValidatorV3{}
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema, fldPath.Child("openAPIV3Schema"), openAPIV3Schema)...)
}
// if validation passed otherwise, make sure we can actually construct a schema validator from this custom resource validation.
if len(allErrs) == 0 {
if _, _, err := apiservervalidation.NewSchemaValidator(customResourceValidation); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, "", fmt.Sprintf("error building validator: %v", err)))
}
}
return allErrs
}
// ValidateCustomResourceDefinitionOpenAPISchema statically validates
func ValidateCustomResourceDefinitionOpenAPISchema(schema *apiextensions.JSONSchemaProps, fldPath *field.Path, ssv specStandardValidator) field.ErrorList {
allErrs := field.ErrorList{}
if schema == nil {
return allErrs
}
allErrs = append(allErrs, ssv.validate(schema, fldPath)...)
if schema.UniqueItems == true {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("uniqueItems"), "uniqueItems cannot be set to true since the runtime complexity becomes quadratic"))
}
// additionalProperties and properties are mutual exclusive because otherwise they
// contradict Kubernetes' API convention to ignore unknown fields.
//
// In other words:
// - properties are for structs,
// - additionalProperties are for map[string]interface{}
//
// Note: when patternProperties is added to OpenAPI some day, this will have to be
// restricted like additionalProperties.
if schema.AdditionalProperties != nil {
if len(schema.Properties) != 0 {
if schema.AdditionalProperties.Allows == false || schema.AdditionalProperties.Schema != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "additionalProperties and properties are mutual exclusive"))
}
}
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema.AdditionalProperties.Schema, fldPath.Child("additionalProperties"), ssv)...)
}
if len(schema.Properties) != 0 {
for property, jsonSchema := range schema.Properties {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("properties").Key(property), ssv)...)
}
}
if len(schema.PatternProperties) != 0 {
for property, jsonSchema := range schema.PatternProperties {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("patternProperties").Key(property), ssv)...)
}
}
if schema.AdditionalItems != nil {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema.AdditionalItems.Schema, fldPath.Child("additionalItems"), ssv)...)
}
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema.Not, fldPath.Child("not"), ssv)...)
if len(schema.AllOf) != 0 {
for i, jsonSchema := range schema.AllOf {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("allOf").Index(i), ssv)...)
}
}
if len(schema.OneOf) != 0 {
for i, jsonSchema := range schema.OneOf {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("oneOf").Index(i), ssv)...)
}
}
if len(schema.AnyOf) != 0 {
for i, jsonSchema := range schema.AnyOf {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("anyOf").Index(i), ssv)...)
}
}
if len(schema.Definitions) != 0 {
for definition, jsonSchema := range schema.Definitions {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("definitions").Key(definition), ssv)...)
}
}
if schema.Items != nil {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema.Items.Schema, fldPath.Child("items"), ssv)...)
if len(schema.Items.JSONSchemas) != 0 {
for i, jsonSchema := range schema.Items.JSONSchemas {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(&jsonSchema, fldPath.Child("items").Index(i), ssv)...)
}
}
}
if schema.Dependencies != nil {
for dependency, jsonSchemaPropsOrStringArray := range schema.Dependencies {
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(jsonSchemaPropsOrStringArray.Schema, fldPath.Child("dependencies").Key(dependency), ssv)...)
}
}
return allErrs
}
type specStandardValidatorV3 struct{}
// validate validates against OpenAPI Schema v3.
func (v *specStandardValidatorV3) validate(schema *apiextensions.JSONSchemaProps, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if schema == nil {
return allErrs
}
if schema.Default != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("default"), "default is not supported"))
}
if schema.ID != "" {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("id"), "id is not supported"))
}
if schema.AdditionalItems != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalItems"), "additionalItems is not supported"))
}
if len(schema.PatternProperties) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("patternProperties"), "patternProperties is not supported"))
}
if len(schema.Definitions) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("definitions"), "definitions is not supported"))
}
if schema.Dependencies != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("dependencies"), "dependencies is not supported"))
}
if schema.Ref != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("$ref"), "$ref is not supported"))
}
if schema.Type == "null" {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("type"), "type cannot be set to null"))
}
if schema.Items != nil && len(schema.Items.JSONSchemas) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("items"), "items must be a schema object and not an array"))
}
return allErrs
}
// ValidateCustomResourceDefinitionSubresources statically validates
func ValidateCustomResourceDefinitionSubresources(subresources *apiextensions.CustomResourceSubresources, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if subresources == nil {
return allErrs
}
if subresources.Scale != nil {
if len(subresources.Scale.SpecReplicasPath) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("scale.specReplicasPath"), ""))
} else {
// should be constrained json path under .spec
if errs := validateSimpleJSONPath(subresources.Scale.SpecReplicasPath, fldPath.Child("scale.specReplicasPath")); len(errs) > 0 {
allErrs = append(allErrs, errs...)
} else if !strings.HasPrefix(subresources.Scale.SpecReplicasPath, ".spec.") {
allErrs = append(allErrs, field.Invalid(fldPath.Child("scale.specReplicasPath"), subresources.Scale.SpecReplicasPath, "should be a json path under .spec"))
}
}
if len(subresources.Scale.StatusReplicasPath) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("scale.statusReplicasPath"), ""))
} else {
// should be constrained json path under .status
if errs := validateSimpleJSONPath(subresources.Scale.StatusReplicasPath, fldPath.Child("scale.statusReplicasPath")); len(errs) > 0 {
allErrs = append(allErrs, errs...)
} else if !strings.HasPrefix(subresources.Scale.StatusReplicasPath, ".status.") {
allErrs = append(allErrs, field.Invalid(fldPath.Child("scale.statusReplicasPath"), subresources.Scale.StatusReplicasPath, "should be a json path under .status"))
}
}
// if labelSelectorPath is present, it should be a constrained json path under .status
if subresources.Scale.LabelSelectorPath != nil && len(*subresources.Scale.LabelSelectorPath) > 0 {
if errs := validateSimpleJSONPath(*subresources.Scale.LabelSelectorPath, fldPath.Child("scale.labelSelectorPath")); len(errs) > 0 {
allErrs = append(allErrs, errs...)
} else if !strings.HasPrefix(*subresources.Scale.LabelSelectorPath, ".status.") {
allErrs = append(allErrs, field.Invalid(fldPath.Child("scale.labelSelectorPath"), subresources.Scale.LabelSelectorPath, "should be a json path under .status"))
}
}
}
return allErrs
}
func validateSimpleJSONPath(s string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
switch {
case len(s) == 0:
allErrs = append(allErrs, field.Invalid(fldPath, s, "must not be empty"))
case s[0] != '.':
allErrs = append(allErrs, field.Invalid(fldPath, s, "must be a simple json path starting with ."))
case s != ".":
if cs := strings.Split(s[1:], "."); len(cs) < 1 {
allErrs = append(allErrs, field.Invalid(fldPath, s, "must be a json path in the dot notation"))
}
}
return allErrs
}
var allowedFieldsAtRootSchema = []string{"Description", "Type", "Format", "Title", "Maximum", "ExclusiveMaximum", "Minimum", "ExclusiveMinimum", "MaxLength", "MinLength", "Pattern", "MaxItems", "MinItems", "UniqueItems", "MultipleOf", "Required", "Items", "Properties", "ExternalDocs", "Example"}
func allowedAtRootSchema(field string) bool {
for _, v := range allowedFieldsAtRootSchema {
if field == v {
return true
}
}
return false
}

Some files were not shown because too many files have changed in this diff Show More