diff --git a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go index e70c823b7f7..cec560097d5 100644 --- a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go +++ b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go @@ -77,6 +77,7 @@ func New() *Generator { `k8s.io/kubernetes/pkg/apis/rbac/v1alpha1`, `k8s.io/kubernetes/federation/apis/federation/v1beta1`, `k8s.io/kubernetes/pkg/apis/certificates/v1alpha1`, + `k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1`, }, ","), DropEmbeddedFields: "k8s.io/kubernetes/pkg/api/unversioned.TypeMeta", } diff --git a/hack/.linted_packages b/hack/.linted_packages index b5aa4116216..6b47894abf4 100644 --- a/hack/.linted_packages +++ b/hack/.linted_packages @@ -70,6 +70,7 @@ pkg/apis/extensions/install pkg/apis/extensions/v1beta1 pkg/apis/policy/install pkg/apis/rbac/install +pkg/apis/imagepolicy/install pkg/api/v1 pkg/auth/authenticator pkg/auth/authorizer/union diff --git a/hack/make-rules/test.sh b/hack/make-rules/test.sh index 5a336c0fca7..6ad4af19961 100755 --- a/hack/make-rules/test.sh +++ b/hack/make-rules/test.sh @@ -66,7 +66,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-} # "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3" # FIXME: due to current implementation of a test client (see: pkg/api/testapi/testapi.go) # ONLY the last version is tested in each group. -KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,batch/v2alpha1,certificates/v1alpha1,extensions/v1beta1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1"} +KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,batch/v2alpha1,certificates/v1alpha1,extensions/v1beta1,federation/v1beta1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,imagepolicy.k8s.io/v1alpha1"} # once we have multiple group supports # Create a junit-style XML test report in this directory if set. KUBE_JUNIT_REPORT_DIR=${KUBE_JUNIT_REPORT_DIR:-} @@ -156,6 +156,8 @@ junitFilenamePrefix() { fi mkdir -p "${KUBE_JUNIT_REPORT_DIR}" local KUBE_TEST_API_NO_SLASH="${KUBE_TEST_API//\//-}" + # This file name isn't parsed by anything, and tee needs a shorter file name. + KUBE_TEST_API_NO_SLASH="${KUBE_TEST_API_NO_SLASH//k8s.io-/}" echo "${KUBE_JUNIT_REPORT_DIR}/junit_${KUBE_TEST_API_NO_SLASH}_$(kube::util::sortable_date)" } diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index 7cd00d2b770..a2b9c3b1f7b 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/imagepolicy" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/runtime" @@ -49,6 +50,7 @@ import ( _ "k8s.io/kubernetes/pkg/apis/certificates/install" _ "k8s.io/kubernetes/pkg/apis/componentconfig/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" + _ "k8s.io/kubernetes/pkg/apis/imagepolicy/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" ) @@ -64,6 +66,7 @@ var ( Federation TestGroup Rbac TestGroup Certificates TestGroup + ImagePolicy TestGroup serializer runtime.SerializerInfo storageSerializer runtime.SerializerInfo @@ -225,6 +228,16 @@ func init() { } } + if _, ok := Groups[imagepolicy.GroupName]; !ok { + externalGroupVersion := unversioned.GroupVersion{Group: imagepolicy.GroupName, Version: registered.GroupOrDie(imagepolicy.GroupName).GroupVersion.Version} + Groups[imagepolicy.GroupName] = TestGroup{ + externalGroupVersion: externalGroupVersion, + internalGroupVersion: imagepolicy.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(imagepolicy.SchemeGroupVersion), + externalTypes: api.Scheme.KnownTypes(externalGroupVersion), + } + } + Default = Groups[api.GroupName] Autoscaling = Groups[autoscaling.GroupName] Batch = Groups[batch.GroupName] @@ -234,6 +247,7 @@ func init() { Extensions = Groups[extensions.GroupName] Federation = Groups[federation.GroupName] Rbac = Groups[rbac.GroupName] + ImagePolicy = Groups[imagepolicy.GroupName] } func (g TestGroup) ContentConfig() (string, *unversioned.GroupVersion, runtime.Codec) { diff --git a/pkg/apis/imagepolicy/doc.go b/pkg/apis/imagepolicy/doc.go new file mode 100644 index 00000000000..cf47b047ec0 --- /dev/null +++ b/pkg/apis/imagepolicy/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package,register +// +groupName=imagepolicy.k8s.io + +package imagepolicy // import "k8s.io/kubernetes/pkg/apis/imagepolicy" diff --git a/pkg/apis/imagepolicy/install/install.go b/pkg/apis/imagepolicy/install/install.go new file mode 100644 index 00000000000..29a0b7ae2dd --- /dev/null +++ b/pkg/apis/imagepolicy/install/install.go @@ -0,0 +1,123 @@ +/* +Copyright 2016 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 installs the experimental API group, making it available as +// an option to all of the API encoding/decoding machinery. +package install + +import ( + "fmt" + + "github.com/golang/glog" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/apis/imagepolicy" + "k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" +) + +const importPrefix = "k8s.io/kubernetes/pkg/apis/imagepolicy" + +var accessor = meta.NewAccessor() + +// availableVersions lists all known external versions for this group from most preferred to least preferred +var availableVersions = []unversioned.GroupVersion{v1alpha1.SchemeGroupVersion} + +func init() { + registered.RegisterVersions(availableVersions) + externalVersions := []unversioned.GroupVersion{} + for _, v := range availableVersions { + if registered.IsAllowedVersion(v) { + externalVersions = append(externalVersions, v) + } + } + if len(externalVersions) == 0 { + glog.V(4).Infof("No version is registered for group %v", imagepolicy.GroupName) + return + } + + if err := registered.EnableVersions(externalVersions...); err != nil { + glog.V(4).Infof("%v", err) + return + } + if err := enableVersions(externalVersions); err != nil { + glog.V(4).Infof("%v", err) + return + } +} + +// TODO: enableVersions should be centralized rather than spread in each API +// group. +// We can combine registered.RegisterVersions, registered.EnableVersions and +// registered.RegisterGroup once we have moved enableVersions there. +func enableVersions(externalVersions []unversioned.GroupVersion) error { + addVersionsToScheme(externalVersions...) + preferredExternalVersion := externalVersions[0] + + groupMeta := apimachinery.GroupMeta{ + GroupVersion: preferredExternalVersion, + GroupVersions: externalVersions, + RESTMapper: newRESTMapper(externalVersions), + SelfLinker: runtime.SelfLinker(accessor), + InterfacesFor: interfacesFor, + } + + if err := registered.RegisterGroup(groupMeta); err != nil { + return err + } + api.RegisterRESTMapper(groupMeta.RESTMapper) + return nil +} + +func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { + // add the internal version to Scheme + imagepolicy.AddToScheme(api.Scheme) + // add the enabled external versions to Scheme + for _, v := range externalVersions { + if !registered.IsEnabledVersion(v) { + glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) + continue + } + switch v { + case v1alpha1.SchemeGroupVersion: + v1alpha1.AddToScheme(api.Scheme) + } + } +} + +func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { + rootScoped := sets.NewString("ImageReview") + ignoredKinds := sets.NewString() + return api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) +} + +func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { + switch version { + case v1alpha1.SchemeGroupVersion: + return &meta.VersionInterfaces{ + ObjectConvertor: api.Scheme, + MetadataAccessor: accessor, + }, nil + default: + g, _ := registered.Group(imagepolicy.GroupName) + return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions) + } +} diff --git a/pkg/apis/imagepolicy/register.go b/pkg/apis/imagepolicy/register.go new file mode 100644 index 00000000000..8f35a3eb880 --- /dev/null +++ b/pkg/apis/imagepolicy/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2016 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 imagepolicy + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name use in this package +const GroupName = "imagepolicy.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) unversioned.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns back a Group qualified GroupResource +func Resource(resource string) unversioned.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &api.ListOptions{}, + &api.DeleteOptions{}, + &api.ExportOptions{}, + + &ImageReview{}, + ) + // versioned.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/imagepolicy/types.generated.go b/pkg/apis/imagepolicy/types.generated.go new file mode 100644 index 00000000000..a908b2786ba --- /dev/null +++ b/pkg/apis/imagepolicy/types.generated.go @@ -0,0 +1 @@ +package imagepolicy diff --git a/pkg/apis/imagepolicy/types.go b/pkg/apis/imagepolicy/types.go new file mode 100644 index 00000000000..0be81f38b9b --- /dev/null +++ b/pkg/apis/imagepolicy/types.go @@ -0,0 +1,67 @@ +/* +Copyright 2016 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 imagepolicy + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" +) + +// +genclient=true +// +nonNamespaced=true +// +noMethods=true + +// ImageReview checks if the set of images in a pod are allowed. +type ImageReview struct { + unversioned.TypeMeta + api.ObjectMeta + + // Spec holds information about the pod being evaluated + Spec ImageReviewSpec + + // Status is filled in by the backend and indicates whether the pod should be allowed. + Status ImageReviewStatus +} + +// ImageReviewSpec is a description of the pod creation request. +type ImageReviewSpec struct { + // Containers is a list of a subset of the information in each container of the Pod being created. + Containers []ImageReviewContainerSpec + // Annotations is a list of key-value pairs extracted from the Pod's annotations. + // It only includes keys which match the pattern `*.image-policy.k8s.io/*`. + // It is up to each webhook backend to determine how to interpret these annotations, if at all. + Annotations map[string]string + // Namespace is the namespace the pod is being created in. + Namespace string +} + +// ImageReviewContainerSpec is a description of a container within the pod creation request. +type ImageReviewContainerSpec struct { + // This can be in the form image:tag or image@SHA:012345679abcdef. + Image string + // In future, we may add command line overrides, exec health check command lines, and so on. +} + +// ImageReviewStatus is the result of the token authentication request. +type ImageReviewStatus struct { + // Allowed indicates that all images were allowed to be run. + Allowed bool + // Reason should be empty unless Allowed is false in which case it + // may contain a short description of what is wrong. Kubernetes + // may truncate excessively long errors when displaying to the user. + Reason string +} diff --git a/pkg/apis/imagepolicy/v1alpha1/doc.go b/pkg/apis/imagepolicy/v1alpha1/doc.go new file mode 100644 index 00000000000..d325b7dfb8e --- /dev/null +++ b/pkg/apis/imagepolicy/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2016 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. +*/ + +// +groupName=imagepolicy.k8s.io +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/imagepolicy + +package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1" diff --git a/pkg/apis/imagepolicy/v1alpha1/register.go b/pkg/apis/imagepolicy/v1alpha1/register.go new file mode 100644 index 00000000000..88848b8ac8a --- /dev/null +++ b/pkg/apis/imagepolicy/v1alpha1/register.go @@ -0,0 +1,46 @@ +/* +Copyright 2016 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 v1alpha1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" +) + +// GroupName is the group name for this API. +const GroupName = "imagepolicy.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha1"} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &v1.ListOptions{}, + &v1.DeleteOptions{}, + &v1.ExportOptions{}, + + &ImageReview{}, + ) + return nil +} diff --git a/pkg/apis/imagepolicy/v1alpha1/types.generated.go b/pkg/apis/imagepolicy/v1alpha1/types.generated.go new file mode 100644 index 00000000000..1c267880d8d --- /dev/null +++ b/pkg/apis/imagepolicy/v1alpha1/types.generated.go @@ -0,0 +1 @@ +package v1alpha1 diff --git a/pkg/apis/imagepolicy/v1alpha1/types.go b/pkg/apis/imagepolicy/v1alpha1/types.go new file mode 100644 index 00000000000..ef9fc544d34 --- /dev/null +++ b/pkg/apis/imagepolicy/v1alpha1/types.go @@ -0,0 +1,67 @@ +/* +Copyright 2016 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 v1alpha1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" +) + +// +genclient=true +// +nonNamespaced=true +// +noMethods=true + +// ImageReview checks if the set of images in a pod are allowed. +type ImageReview struct { + unversioned.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Spec holds information about the pod being evaluated + Spec ImageReviewSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` + + // Status is filled in by the backend and indicates whether the pod should be allowed. + Status ImageReviewStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// ImageReviewSpec is a description of the pod creation request. +type ImageReviewSpec struct { + // Containers is a list of a subset of the information in each container of the Pod being created. + Containers []ImageReviewContainerSpec `json:"containers,omitempty" protobuf:"bytes,1,rep,name=containers"` + // Annotations is a list of key-value pairs extracted from the Pod's annotations. + // It only includes keys which match the pattern `*.image-policy.k8s.io/*`. + // It is up to each webhook backend to determine how to interpret these annotations, if at all. + Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,2,rep,name=annotations"` + // Namespace is the namespace the pod is being created in. + Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"` +} + +// ImageReviewContainerSpec is a description of a container within the pod creation request. +type ImageReviewContainerSpec struct { + // This can be in the form image:tag or image@SHA:012345679abcdef. + Image string `json:"image,omitempty" protobuf:"bytes,1,opt,name=image"` + // In future, we may add command line overrides, exec health check command lines, and so on. +} + +// ImageReviewStatus is the result of the token authentication request. +type ImageReviewStatus struct { + // Allowed indicates that all images were allowed to be run. + Allowed bool `json:"allowed" protobuf:"varint,1,opt,name=allowed"` + // Reason should be empty unless Allowed is false in which case it + // may contain a short description of what is wrong. Kubernetes + // may truncate excessively long errors when displaying to the user. + Reason string `json:"reason,omitempty" protobuf:"bytes,2,opt,name=reason"` +}