diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 1a087b736cc..dc287771fcc 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -57,6 +57,7 @@ import ( "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" @@ -471,8 +472,9 @@ func BuildStorageFactory(s *options.ServerRunOptions) (*serverstorage.DefaultSto return nil, fmt.Errorf("error in initializing storage factory: %s", err) } - // keep Deployments in extensions for backwards compatibility, we'll have to migrate at some point, eventually + // keep Deployments and NetworkPolicies in extensions for backwards compatibility, we'll have to migrate at some point, eventually storageFactory.AddCohabitatingResources(extensions.Resource("deployments"), apps.Resource("deployments")) + storageFactory.AddCohabitatingResources(extensions.Resource("networkpolicies"), networking.Resource("networkpolicies")) for _, override := range s.Etcd.EtcdServersOverrides { tokens := strings.Split(override, "#") if len(tokens) != 2 { diff --git a/cmd/libs/go2idl/client-gen/main.go b/cmd/libs/go2idl/client-gen/main.go index 4bb4c160a24..77ac1cf7872 100644 --- a/cmd/libs/go2idl/client-gen/main.go +++ b/cmd/libs/go2idl/client-gen/main.go @@ -48,6 +48,7 @@ var ( "apps/", "policy/", "settings/", + "networking/", }, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\".") includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.") basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group.") diff --git a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go index a9c1e390d9f..b33641bbb45 100644 --- a/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go +++ b/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go @@ -95,6 +95,7 @@ func New() *Generator { `-k8s.io/client-go/pkg/api/v1`, `k8s.io/metrics/pkg/apis/metrics/v1alpha1`, `k8s.io/metrics/pkg/apis/custom_metrics/v1alpha1`, + `k8s.io/kubernetes/pkg/apis/networking/v1`, }, ","), DropEmbeddedFields: "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta", } diff --git a/hack/.linted_packages b/hack/.linted_packages index 24c0e0ebda1..2fde0efd57d 100644 --- a/hack/.linted_packages +++ b/hack/.linted_packages @@ -81,6 +81,8 @@ pkg/apis/componentconfig/install pkg/apis/extensions/install pkg/apis/extensions/v1beta1 pkg/apis/imagepolicy/install +pkg/apis/networking/install +pkg/apis/networking/validation pkg/apis/policy/install pkg/apis/rbac/install pkg/apis/rbac/v1alpha1 diff --git a/hack/lib/init.sh b/hack/lib/init.sh old mode 100644 new mode 100755 index 2f6a59201c4..41967e1c8ea --- a/hack/lib/init.sh +++ b/hack/lib/init.sh @@ -65,6 +65,7 @@ batch/v2alpha1 \ certificates.k8s.io/v1beta1 \ extensions/v1beta1 \ imagepolicy.k8s.io/v1alpha1 \ +networking.k8s.io/v1 \ policy/v1beta1 \ rbac.authorization.k8s.io/v1beta1 \ rbac.authorization.k8s.io/v1alpha1 \ diff --git a/pkg/api/defaulting_test.go b/pkg/api/defaulting_test.go index f850fd8b160..6a11771d2c3 100644 --- a/pkg/api/defaulting_test.go +++ b/pkg/api/defaulting_test.go @@ -125,6 +125,8 @@ func TestDefaulting(t *testing.T) { {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "InitializerConfigurationList"}: {}, {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ExternalAdmissionHookConfiguration"}: {}, {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ExternalAdmissionHookConfigurationList"}: {}, + {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}: {}, + {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}: {}, } f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1)) diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index c7e7dbc9366..d36efa95670 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -44,6 +44,7 @@ import ( "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/imagepolicy" + "k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/settings" @@ -61,6 +62,7 @@ import ( _ "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/networking/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" _ "k8s.io/kubernetes/pkg/apis/settings/install" @@ -82,6 +84,7 @@ var ( Settings TestGroup Storage TestGroup ImagePolicy TestGroup + Networking TestGroup serializer runtime.SerializerInfo storageSerializer runtime.SerializerInfo @@ -289,6 +292,16 @@ func init() { externalTypes: api.Scheme.KnownTypes(externalGroupVersion), } } + if _, ok := Groups[networking.GroupName]; !ok { + externalGroupVersion := schema.GroupVersion{Group: networking.GroupName, Version: api.Registry.GroupOrDie(networking.GroupName).GroupVersion.Version} + Groups[networking.GroupName] = TestGroup{ + externalGroupVersion: externalGroupVersion, + internalGroupVersion: networking.SchemeGroupVersion, + internalTypes: api.Scheme.KnownTypes(networking.SchemeGroupVersion), + externalTypes: api.Scheme.KnownTypes(externalGroupVersion), + } + } + Default = Groups[api.GroupName] Autoscaling = Groups[autoscaling.GroupName] Batch = Groups[batch.GroupName] @@ -302,6 +315,7 @@ func init() { Storage = Groups[storage.GroupName] ImagePolicy = Groups[imagepolicy.GroupName] Authorization = Groups[authorization.GroupName] + Networking = Groups[networking.GroupName] } func (g TestGroup) ContentConfig() (string, *schema.GroupVersion, runtime.Codec) { diff --git a/pkg/apis/extensions/types.go b/pkg/apis/extensions/types.go index 7c2c1c2856e..b0b32c28b8c 100644 --- a/pkg/apis/extensions/types.go +++ b/pkg/apis/extensions/types.go @@ -1042,6 +1042,7 @@ type PodSecurityPolicyList struct { // +genclient=true +// NetworkPolicy describes what network traffic is allowed for a set of Pods type NetworkPolicy struct { metav1.TypeMeta // +optional @@ -1061,13 +1062,12 @@ type NetworkPolicySpec struct { PodSelector metav1.LabelSelector // List of ingress rules to be applied to the selected pods. - // Traffic is allowed to a pod if namespace.networkPolicy.ingress.isolation is undefined and cluster policy allows it, + // Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod // OR if the traffic source is the pod's local node, // OR if the traffic matches at least one ingress rule across all of the NetworkPolicy // objects whose podSelector matches the pod. - // If this field is empty then this NetworkPolicy does not affect ingress isolation. - // If this field is present and contains at least one rule, this policy allows any traffic - // which matches at least one of the ingress rules in this list. + // If this field is empty then this NetworkPolicy does not allow any traffic + // (and serves solely to ensure that the pods it selects are isolated by default). // +optional Ingress []NetworkPolicyIngressRule } diff --git a/pkg/apis/extensions/v1beta1/conversion.go b/pkg/apis/extensions/v1beta1/conversion.go index 38b8c2ac8f8..afbe42f67b0 100644 --- a/pkg/apis/extensions/v1beta1/conversion.go +++ b/pkg/apis/extensions/v1beta1/conversion.go @@ -23,8 +23,10 @@ import ( "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/kubernetes/pkg/api" v1 "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/networking" ) func addConversionFuncs(scheme *runtime.Scheme) error { @@ -42,6 +44,18 @@ func addConversionFuncs(scheme *runtime.Scheme) error { Convert_v1beta1_RollingUpdateDaemonSet_To_extensions_RollingUpdateDaemonSet, Convert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec, Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec, + Convert_v1beta1_NetworkPolicy_To_networking_NetworkPolicy, + Convert_networking_NetworkPolicy_To_v1beta1_NetworkPolicy, + Convert_v1beta1_NetworkPolicyIngressRule_To_networking_NetworkPolicyIngressRule, + Convert_networking_NetworkPolicyIngressRule_To_v1beta1_NetworkPolicyIngressRule, + Convert_v1beta1_NetworkPolicyList_To_networking_NetworkPolicyList, + Convert_networking_NetworkPolicyList_To_v1beta1_NetworkPolicyList, + Convert_v1beta1_NetworkPolicyPeer_To_networking_NetworkPolicyPeer, + Convert_networking_NetworkPolicyPeer_To_v1beta1_NetworkPolicyPeer, + Convert_v1beta1_NetworkPolicyPort_To_networking_NetworkPolicyPort, + Convert_networking_NetworkPolicyPort_To_v1beta1_NetworkPolicyPort, + Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec, + Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec, ) if err != nil { return err @@ -260,3 +274,155 @@ func Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *ReplicaSetS } return nil } + +func Convert_v1beta1_NetworkPolicy_To_networking_NetworkPolicy(in *NetworkPolicy, out *networking.NetworkPolicy, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + return Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec(&in.Spec, &out.Spec, s) +} + +func Convert_networking_NetworkPolicy_To_v1beta1_NetworkPolicy(in *networking.NetworkPolicy, out *NetworkPolicy, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + return Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec(&in.Spec, &out.Spec, s) +} + +func Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec(in *NetworkPolicySpec, out *networking.NetworkPolicySpec, s conversion.Scope) error { + if err := s.Convert(&in.PodSelector, &out.PodSelector, 0); err != nil { + return err + } + out.Ingress = make([]networking.NetworkPolicyIngressRule, len(in.Ingress)) + for i := range in.Ingress { + if err := Convert_v1beta1_NetworkPolicyIngressRule_To_networking_NetworkPolicyIngressRule(&in.Ingress[i], &out.Ingress[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec(in *networking.NetworkPolicySpec, out *NetworkPolicySpec, s conversion.Scope) error { + if err := s.Convert(&in.PodSelector, &out.PodSelector, 0); err != nil { + return err + } + out.Ingress = make([]NetworkPolicyIngressRule, len(in.Ingress)) + for i := range in.Ingress { + if err := Convert_networking_NetworkPolicyIngressRule_To_v1beta1_NetworkPolicyIngressRule(&in.Ingress[i], &out.Ingress[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_NetworkPolicyIngressRule_To_networking_NetworkPolicyIngressRule(in *NetworkPolicyIngressRule, out *networking.NetworkPolicyIngressRule, s conversion.Scope) error { + out.Ports = make([]networking.NetworkPolicyPort, len(in.Ports)) + for i := range in.Ports { + if err := Convert_v1beta1_NetworkPolicyPort_To_networking_NetworkPolicyPort(&in.Ports[i], &out.Ports[i], s); err != nil { + return err + } + } + out.From = make([]networking.NetworkPolicyPeer, len(in.From)) + for i := range in.From { + if err := Convert_v1beta1_NetworkPolicyPeer_To_networking_NetworkPolicyPeer(&in.From[i], &out.From[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_networking_NetworkPolicyIngressRule_To_v1beta1_NetworkPolicyIngressRule(in *networking.NetworkPolicyIngressRule, out *NetworkPolicyIngressRule, s conversion.Scope) error { + out.Ports = make([]NetworkPolicyPort, len(in.Ports)) + for i := range in.Ports { + if err := Convert_networking_NetworkPolicyPort_To_v1beta1_NetworkPolicyPort(&in.Ports[i], &out.Ports[i], s); err != nil { + return err + } + } + out.From = make([]NetworkPolicyPeer, len(in.From)) + for i := range in.From { + if err := Convert_networking_NetworkPolicyPeer_To_v1beta1_NetworkPolicyPeer(&in.From[i], &out.From[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_v1beta1_NetworkPolicyPeer_To_networking_NetworkPolicyPeer(in *NetworkPolicyPeer, out *networking.NetworkPolicyPeer, s conversion.Scope) error { + if in.PodSelector != nil { + out.PodSelector = new(metav1.LabelSelector) + if err := s.Convert(in.PodSelector, out.PodSelector, 0); err != nil { + return err + } + } else { + out.PodSelector = nil + } + if in.NamespaceSelector != nil { + out.NamespaceSelector = new(metav1.LabelSelector) + if err := s.Convert(in.NamespaceSelector, out.NamespaceSelector, 0); err != nil { + return err + } + } else { + out.NamespaceSelector = nil + } + return nil +} + +func Convert_networking_NetworkPolicyPeer_To_v1beta1_NetworkPolicyPeer(in *networking.NetworkPolicyPeer, out *NetworkPolicyPeer, s conversion.Scope) error { + if in.PodSelector != nil { + out.PodSelector = new(metav1.LabelSelector) + if err := s.Convert(in.PodSelector, out.PodSelector, 0); err != nil { + return err + } + } else { + out.PodSelector = nil + } + if in.NamespaceSelector != nil { + out.NamespaceSelector = new(metav1.LabelSelector) + if err := s.Convert(in.NamespaceSelector, out.NamespaceSelector, 0); err != nil { + return err + } + } else { + out.NamespaceSelector = nil + } + return nil +} + +func Convert_v1beta1_NetworkPolicyPort_To_networking_NetworkPolicyPort(in *NetworkPolicyPort, out *networking.NetworkPolicyPort, s conversion.Scope) error { + if in.Protocol != nil { + out.Protocol = new(api.Protocol) + *out.Protocol = api.Protocol(*in.Protocol) + } else { + out.Protocol = nil + } + out.Port = in.Port + return nil +} + +func Convert_networking_NetworkPolicyPort_To_v1beta1_NetworkPolicyPort(in *networking.NetworkPolicyPort, out *NetworkPolicyPort, s conversion.Scope) error { + if in.Protocol != nil { + out.Protocol = new(v1.Protocol) + *out.Protocol = v1.Protocol(*in.Protocol) + } else { + out.Protocol = nil + } + out.Port = in.Port + return nil +} + +func Convert_v1beta1_NetworkPolicyList_To_networking_NetworkPolicyList(in *NetworkPolicyList, out *networking.NetworkPolicyList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = make([]networking.NetworkPolicy, len(in.Items)) + for i := range in.Items { + if err := Convert_v1beta1_NetworkPolicy_To_networking_NetworkPolicy(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_networking_NetworkPolicyList_To_v1beta1_NetworkPolicyList(in *networking.NetworkPolicyList, out *NetworkPolicyList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = make([]NetworkPolicy, len(in.Items)) + for i := range in.Items { + if err := Convert_networking_NetworkPolicy_To_v1beta1_NetworkPolicy(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + return nil +} diff --git a/pkg/apis/extensions/v1beta1/doc.go b/pkg/apis/extensions/v1beta1/doc.go index f4e544e67d4..0b074d04693 100644 --- a/pkg/apis/extensions/v1beta1/doc.go +++ b/pkg/apis/extensions/v1beta1/doc.go @@ -18,6 +18,7 @@ limitations under the License. // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/extensions // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/autoscaling // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/batch +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/networking // +k8s:openapi-gen=true // +k8s:defaulter-gen=TypeMeta diff --git a/pkg/apis/extensions/v1beta1/types.go b/pkg/apis/extensions/v1beta1/types.go index 196f3137538..47156eec52c 100644 --- a/pkg/apis/extensions/v1beta1/types.go +++ b/pkg/apis/extensions/v1beta1/types.go @@ -1055,6 +1055,7 @@ type PodSecurityPolicyList struct { Items []PodSecurityPolicy `json:"items" protobuf:"bytes,2,rep,name=items"` } +// NetworkPolicy describes what network traffic is allowed for a set of Pods type NetworkPolicy struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -1076,13 +1077,12 @@ type NetworkPolicySpec struct { PodSelector metav1.LabelSelector `json:"podSelector" protobuf:"bytes,1,opt,name=podSelector"` // List of ingress rules to be applied to the selected pods. - // Traffic is allowed to a pod if namespace.networkPolicy.ingress.isolation is undefined and cluster policy allows it, + // Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod // OR if the traffic source is the pod's local node, // OR if the traffic matches at least one ingress rule across all of the NetworkPolicy // objects whose podSelector matches the pod. - // If this field is empty then this NetworkPolicy does not affect ingress isolation. - // If this field is present and contains at least one rule, this policy allows any traffic - // which matches at least one of the ingress rules in this list. + // If this field is empty then this NetworkPolicy does not allow any traffic + // (and serves solely to ensure that the pods it selects are isolated by default). // +optional Ingress []NetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,2,rep,name=ingress"` } diff --git a/pkg/apis/networking/OWNERS b/pkg/apis/networking/OWNERS new file mode 100755 index 00000000000..97bde97282c --- /dev/null +++ b/pkg/apis/networking/OWNERS @@ -0,0 +1,4 @@ +reviewers: +- caseydavenport +- danwinship +- thockin diff --git a/pkg/apis/networking/doc.go b/pkg/apis/networking/doc.go new file mode 100644 index 00000000000..b583051ca4c --- /dev/null +++ b/pkg/apis/networking/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package,register +// +groupName=networking.k8s.io +package networking // import "k8s.io/kubernetes/pkg/apis/networking" diff --git a/pkg/apis/networking/install/install.go b/pkg/apis/networking/install/install.go new file mode 100644 index 00000000000..1d118c76804 --- /dev/null +++ b/pkg/apis/networking/install/install.go @@ -0,0 +1,49 @@ +/* +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 installs the experimental API group, making it available as +// an option to all of the API encoding/decoding machinery. +package install + +import ( + "k8s.io/apimachinery/pkg/apimachinery/announced" + "k8s.io/apimachinery/pkg/apimachinery/registered" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/apis/networking/v1" +) + +func init() { + Install(api.GroupFactoryRegistry, api.Registry, api.Scheme) +} + +// Install registers the API group and adds types to a scheme +func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) { + if err := announced.NewGroupMetaFactory( + &announced.GroupMetaFactoryArgs{ + GroupName: networking.GroupName, + VersionPreferenceOrder: []string{v1.SchemeGroupVersion.Version}, + ImportPrefix: "k8s.io/kubernetes/pkg/apis/networking", + AddInternalObjectsToScheme: networking.AddToScheme, + }, + announced.VersionToSchemeFunc{ + v1.SchemeGroupVersion.Version: v1.AddToScheme, + }, + ).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil { + panic(err) + } +} diff --git a/pkg/apis/networking/register.go b/pkg/apis/networking/register.go new file mode 100644 index 00000000000..4d58a210da2 --- /dev/null +++ b/pkg/apis/networking/register.go @@ -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 networking + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "networking.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 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 +) + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &NetworkPolicy{}, + &NetworkPolicyList{}, + ) + return nil +} diff --git a/pkg/apis/networking/types.go b/pkg/apis/networking/types.go new file mode 100644 index 00000000000..f889543ed75 --- /dev/null +++ b/pkg/apis/networking/types.go @@ -0,0 +1,115 @@ +/* +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 networking + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/kubernetes/pkg/api" +) + +// +genclient=true + +// NetworkPolicy describes what network traffic is allowed for a set of Pods +type NetworkPolicy struct { + metav1.TypeMeta + // +optional + metav1.ObjectMeta + + // Specification of the desired behavior for this NetworkPolicy. + // +optional + Spec NetworkPolicySpec +} + +// NetworkPolicySpec provides the specification of a NetworkPolicy +type NetworkPolicySpec struct { + // Selects the pods to which this NetworkPolicy object applies. The array of + // ingress rules is applied to any pods selected by this field. Multiple network + // policies can select the same set of pods. In this case, the ingress rules for + // each are combined additively. This field is NOT optional and follows standard + // label selector semantics. An empty podSelector matches all pods in this + // namespace. + PodSelector metav1.LabelSelector + + // List of ingress rules to be applied to the selected pods. Traffic is allowed to + // a pod if there are no NetworkPolicies selecting the pod + // (and cluster policy otherwise allows the traffic), OR if the traffic source is + // the pod's local node, OR if the traffic matches at least one ingress rule + // across all of the NetworkPolicy objects whose podSelector matches the pod. If + // this field is empty then this NetworkPolicy does not allow any traffic (and serves + // solely to ensure that the pods it selects are isolated by default) + // +optional + Ingress []NetworkPolicyIngressRule +} + +// NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods +// matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. +type NetworkPolicyIngressRule struct { + // List of ports which should be made accessible on the pods selected for this + // rule. Each item in this list is combined using a logical OR. If this field is + // empty or missing, this rule matches all ports (traffic not restricted by port). + // If this field is present and contains at least one item, then this rule allows + // traffic only if the traffic matches at least one port in the list. + // +optional + Ports []NetworkPolicyPort + + // List of sources which should be able to access the pods selected for this rule. + // Items in this list are combined using a logical OR operation. If this field is + // empty or missing, this rule matches all sources (traffic not restricted by + // source). If this field is present and contains at least on item, this rule + // allows traffic only if the traffic matches at least one item in the from list. + // +optional + From []NetworkPolicyPeer +} + +// NetworkPolicyPort describes a port to allow traffic on +type NetworkPolicyPort struct { + // The protocol (TCP or UDP) which traffic must match. If not specified, this + // field defaults to TCP. + // +optional + Protocol *api.Protocol + + // The port on the given protocol. This can either be a numerical or named port on + // a pod. If this field is not provided, this matches all port names and numbers. + // +optional + Port *intstr.IntOrString +} + +// NetworkPolicyPeer describes a peer to allow traffic from. Exactly one of its fields +// must be specified. +type NetworkPolicyPeer struct { + // This is a label selector which selects Pods in this namespace. This field + // follows standard label selector semantics. If present but empty, this selector + // selects all pods in this namespace. + // +optional + PodSelector *metav1.LabelSelector + + // Selects Namespaces using cluster scoped-labels. This matches all pods in all + // namespaces selected by this label selector. This field follows standard label + // selector semantics. If present but empty, this selector selects all namespaces. + // +optional + NamespaceSelector *metav1.LabelSelector +} + +// NetworkPolicyList is a list of NetworkPolicy objects. +type NetworkPolicyList struct { + metav1.TypeMeta + // +optional + metav1.ListMeta + + Items []NetworkPolicy +} diff --git a/pkg/apis/networking/v1/conversion.go b/pkg/apis/networking/v1/conversion.go new file mode 100644 index 00000000000..b6d1a36df72 --- /dev/null +++ b/pkg/apis/networking/v1/conversion.go @@ -0,0 +1,195 @@ +/* +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/conversion" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/apis/extensions" +) + +func addConversionFuncs(scheme *runtime.Scheme) error { + return scheme.AddConversionFuncs( + Convert_v1_NetworkPolicy_To_extensions_NetworkPolicy, + Convert_extensions_NetworkPolicy_To_v1_NetworkPolicy, + Convert_v1_NetworkPolicyIngressRule_To_extensions_NetworkPolicyIngressRule, + Convert_extensions_NetworkPolicyIngressRule_To_v1_NetworkPolicyIngressRule, + Convert_v1_NetworkPolicyList_To_extensions_NetworkPolicyList, + Convert_extensions_NetworkPolicyList_To_v1_NetworkPolicyList, + Convert_v1_NetworkPolicyPeer_To_extensions_NetworkPolicyPeer, + Convert_extensions_NetworkPolicyPeer_To_v1_NetworkPolicyPeer, + Convert_v1_NetworkPolicyPort_To_extensions_NetworkPolicyPort, + Convert_extensions_NetworkPolicyPort_To_v1_NetworkPolicyPort, + Convert_v1_NetworkPolicySpec_To_extensions_NetworkPolicySpec, + Convert_extensions_NetworkPolicySpec_To_v1_NetworkPolicySpec, + ) +} + +func Convert_v1_NetworkPolicy_To_extensions_NetworkPolicy(in *NetworkPolicy, out *extensions.NetworkPolicy, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + return Convert_v1_NetworkPolicySpec_To_extensions_NetworkPolicySpec(&in.Spec, &out.Spec, s) +} + +func Convert_extensions_NetworkPolicy_To_v1_NetworkPolicy(in *extensions.NetworkPolicy, out *NetworkPolicy, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + return Convert_extensions_NetworkPolicySpec_To_v1_NetworkPolicySpec(&in.Spec, &out.Spec, s) +} + +func Convert_v1_NetworkPolicySpec_To_extensions_NetworkPolicySpec(in *NetworkPolicySpec, out *extensions.NetworkPolicySpec, s conversion.Scope) error { + if err := s.Convert(&in.PodSelector, &out.PodSelector, 0); err != nil { + return err + } + out.Ingress = make([]extensions.NetworkPolicyIngressRule, len(in.Ingress)) + for i := range in.Ingress { + if err := Convert_v1_NetworkPolicyIngressRule_To_extensions_NetworkPolicyIngressRule(&in.Ingress[i], &out.Ingress[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_extensions_NetworkPolicySpec_To_v1_NetworkPolicySpec(in *extensions.NetworkPolicySpec, out *NetworkPolicySpec, s conversion.Scope) error { + if err := s.Convert(&in.PodSelector, &out.PodSelector, 0); err != nil { + return err + } + out.Ingress = make([]NetworkPolicyIngressRule, len(in.Ingress)) + for i := range in.Ingress { + if err := Convert_extensions_NetworkPolicyIngressRule_To_v1_NetworkPolicyIngressRule(&in.Ingress[i], &out.Ingress[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_v1_NetworkPolicyIngressRule_To_extensions_NetworkPolicyIngressRule(in *NetworkPolicyIngressRule, out *extensions.NetworkPolicyIngressRule, s conversion.Scope) error { + out.Ports = make([]extensions.NetworkPolicyPort, len(in.Ports)) + for i := range in.Ports { + if err := Convert_v1_NetworkPolicyPort_To_extensions_NetworkPolicyPort(&in.Ports[i], &out.Ports[i], s); err != nil { + return err + } + } + out.From = make([]extensions.NetworkPolicyPeer, len(in.From)) + for i := range in.From { + if err := Convert_v1_NetworkPolicyPeer_To_extensions_NetworkPolicyPeer(&in.From[i], &out.From[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_extensions_NetworkPolicyIngressRule_To_v1_NetworkPolicyIngressRule(in *extensions.NetworkPolicyIngressRule, out *NetworkPolicyIngressRule, s conversion.Scope) error { + out.Ports = make([]NetworkPolicyPort, len(in.Ports)) + for i := range in.Ports { + if err := Convert_extensions_NetworkPolicyPort_To_v1_NetworkPolicyPort(&in.Ports[i], &out.Ports[i], s); err != nil { + return err + } + } + out.From = make([]NetworkPolicyPeer, len(in.From)) + for i := range in.From { + if err := Convert_extensions_NetworkPolicyPeer_To_v1_NetworkPolicyPeer(&in.From[i], &out.From[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_v1_NetworkPolicyPeer_To_extensions_NetworkPolicyPeer(in *NetworkPolicyPeer, out *extensions.NetworkPolicyPeer, s conversion.Scope) error { + if in.PodSelector != nil { + out.PodSelector = new(metav1.LabelSelector) + if err := s.Convert(in.PodSelector, out.PodSelector, 0); err != nil { + return err + } + } else { + out.PodSelector = nil + } + if in.NamespaceSelector != nil { + out.NamespaceSelector = new(metav1.LabelSelector) + if err := s.Convert(in.NamespaceSelector, out.NamespaceSelector, 0); err != nil { + return err + } + } else { + out.NamespaceSelector = nil + } + return nil +} + +func Convert_extensions_NetworkPolicyPeer_To_v1_NetworkPolicyPeer(in *extensions.NetworkPolicyPeer, out *NetworkPolicyPeer, s conversion.Scope) error { + if in.PodSelector != nil { + out.PodSelector = new(metav1.LabelSelector) + if err := s.Convert(in.PodSelector, out.PodSelector, 0); err != nil { + return err + } + } else { + out.PodSelector = nil + } + if in.NamespaceSelector != nil { + out.NamespaceSelector = new(metav1.LabelSelector) + if err := s.Convert(in.NamespaceSelector, out.NamespaceSelector, 0); err != nil { + return err + } + } else { + out.NamespaceSelector = nil + } + return nil +} + +func Convert_v1_NetworkPolicyPort_To_extensions_NetworkPolicyPort(in *NetworkPolicyPort, out *extensions.NetworkPolicyPort, s conversion.Scope) error { + if in.Protocol != nil { + out.Protocol = new(api.Protocol) + *out.Protocol = api.Protocol(*in.Protocol) + } else { + out.Protocol = nil + } + out.Port = in.Port + return nil +} + +func Convert_extensions_NetworkPolicyPort_To_v1_NetworkPolicyPort(in *extensions.NetworkPolicyPort, out *NetworkPolicyPort, s conversion.Scope) error { + if in.Protocol != nil { + out.Protocol = new(v1.Protocol) + *out.Protocol = v1.Protocol(*in.Protocol) + } else { + out.Protocol = nil + } + out.Port = in.Port + return nil +} + +func Convert_v1_NetworkPolicyList_To_extensions_NetworkPolicyList(in *NetworkPolicyList, out *extensions.NetworkPolicyList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = make([]extensions.NetworkPolicy, len(in.Items)) + for i := range in.Items { + if err := Convert_v1_NetworkPolicy_To_extensions_NetworkPolicy(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + return nil +} + +func Convert_extensions_NetworkPolicyList_To_v1_NetworkPolicyList(in *extensions.NetworkPolicyList, out *NetworkPolicyList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = make([]NetworkPolicy, len(in.Items)) + for i := range in.Items { + if err := Convert_extensions_NetworkPolicy_To_v1_NetworkPolicy(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + return nil +} diff --git a/pkg/apis/networking/v1/defaults.go b/pkg/apis/networking/v1/defaults.go new file mode 100644 index 00000000000..e4aeaaa68b0 --- /dev/null +++ b/pkg/apis/networking/v1/defaults.go @@ -0,0 +1,34 @@ +/* +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 ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/api/v1" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_NetworkPolicyPort(obj *NetworkPolicyPort) { + // Default any undefined Protocol fields to TCP. + if obj.Protocol == nil { + proto := v1.ProtocolTCP + obj.Protocol = &proto + } +} diff --git a/pkg/apis/networking/v1/doc.go b/pkg/apis/networking/v1/doc.go new file mode 100644 index 00000000000..e4000f86c97 --- /dev/null +++ b/pkg/apis/networking/v1/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/networking +// +k8s:openapi-gen=true +// +k8s:defaulter-gen=TypeMeta +// +groupName=networking.k8s.io +package v1 // import "k8s.io/kubernetes/pkg/apis/networking/v1" diff --git a/pkg/apis/networking/v1/register.go b/pkg/apis/networking/v1/register.go new file mode 100644 index 00000000000..c8d3c83bcc9 --- /dev/null +++ b/pkg/apis/networking/v1/register.go @@ -0,0 +1,50 @@ +/* +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" +) + +// GroupName is the group name use in this package +const GroupName = "networking.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// 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, addDefaultingFuncs, addConversionFuncs) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &NetworkPolicy{}, + &NetworkPolicyList{}, + ) + + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/networking/v1/types.generated.go b/pkg/apis/networking/v1/types.generated.go new file mode 100644 index 00000000000..b7b1f993d55 --- /dev/null +++ b/pkg/apis/networking/v1/types.generated.go @@ -0,0 +1 @@ +package v1 diff --git a/pkg/apis/networking/v1/types.go b/pkg/apis/networking/v1/types.go new file mode 100644 index 00000000000..b5b57d34c9a --- /dev/null +++ b/pkg/apis/networking/v1/types.go @@ -0,0 +1,120 @@ +/* +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/util/intstr" + "k8s.io/kubernetes/pkg/api/v1" +) + +// +genclient=true + +// NetworkPolicy describes what network traffic is allowed for a set of Pods +type NetworkPolicy struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Specification of the desired behavior for this NetworkPolicy. + // +optional + Spec NetworkPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` +} + +// NetworkPolicySpec provides the specification of a NetworkPolicy +type NetworkPolicySpec struct { + // Selects the pods to which this NetworkPolicy object applies. The array of + // ingress rules is applied to any pods selected by this field. Multiple network + // policies can select the same set of pods. In this case, the ingress rules for + // each are combined additively. This field is NOT optional and follows standard + // label selector semantics. An empty podSelector matches all pods in this + // namespace. + PodSelector metav1.LabelSelector `json:"podSelector" protobuf:"bytes,1,opt,name=podSelector"` + + // List of ingress rules to be applied to the selected pods. Traffic is allowed to + // a pod if there are no NetworkPolicies selecting the pod + // (and cluster policy otherwise allows the traffic), OR if the traffic source is + // the pod's local node, OR if the traffic matches at least one ingress rule + // across all of the NetworkPolicy objects whose podSelector matches the pod. If + // this field is empty then this NetworkPolicy does not allow any traffic (and serves + // solely to ensure that the pods it selects are isolated by default) + // +optional + Ingress []NetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,2,rep,name=ingress"` +} + +// NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods +// matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. +type NetworkPolicyIngressRule struct { + // List of ports which should be made accessible on the pods selected for this + // rule. Each item in this list is combined using a logical OR. If this field is + // empty or missing, this rule matches all ports (traffic not restricted by port). + // If this field is present and contains at least one item, then this rule allows + // traffic only if the traffic matches at least one port in the list. + // +optional + Ports []NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"` + + // List of sources which should be able to access the pods selected for this rule. + // Items in this list are combined using a logical OR operation. If this field is + // empty or missing, this rule matches all sources (traffic not restricted by + // source). If this field is present and contains at least on item, this rule + // allows traffic only if the traffic matches at least one item in the from list. + // +optional + From []NetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"` +} + +// NetworkPolicyPort describes a port to allow traffic on +type NetworkPolicyPort struct { + // The protocol (TCP or UDP) which traffic must match. If not specified, this + // field defaults to TCP. + // +optional + Protocol *v1.Protocol `json:"protocol,omitempty" protobuf:"bytes,1,opt,name=protocol,casttype=k8s.io/kubernetes/pkg/api/v1.Protocol"` + + // The port on the given protocol. This can either be a numerical or named port on + // a pod. If this field is not provided, this matches all port names and numbers. + // +optional + Port *intstr.IntOrString `json:"port,omitempty" protobuf:"bytes,2,opt,name=port"` +} + +// NetworkPolicyPeer describes a peer to allow traffic from. Exactly one of its fields +// must be specified. +type NetworkPolicyPeer struct { + // This is a label selector which selects Pods in this namespace. This field + // follows standard label selector semantics. If present but empty, this selector + // selects all pods in this namespace. + // +optional + PodSelector *metav1.LabelSelector `json:"podSelector,omitempty" protobuf:"bytes,1,opt,name=podSelector"` + + // Selects Namespaces using cluster scoped-labels. This matches all pods in all + // namespaces selected by this label selector. This field follows standard label + // selector semantics. If present but empty, this selector selects all namespaces. + // +optional + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,2,opt,name=namespaceSelector"` +} + +// NetworkPolicyList is a list of NetworkPolicy objects. +type NetworkPolicyList struct { + metav1.TypeMeta `json:",inline"` + // Standard list metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is a list of schema objects. + Items []NetworkPolicy `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go new file mode 100644 index 00000000000..e2db31430fe --- /dev/null +++ b/pkg/apis/networking/validation/validation.go @@ -0,0 +1,99 @@ +/* +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 ( + "reflect" + + unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/validation" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/kubernetes/pkg/api" + apivalidation "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/apis/networking" +) + +// ValidateNetworkPolicyName can be used to check whether the given networkpolicy +// name is valid. +func ValidateNetworkPolicyName(name string, prefix bool) []string { + return apivalidation.NameIsDNSSubdomain(name, prefix) +} + +// ValidateNetworkPolicySpec tests if required fields in the networkpolicy spec are set. +func ValidateNetworkPolicySpec(spec *networking.NetworkPolicySpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&spec.PodSelector, fldPath.Child("podSelector"))...) + + // Validate ingress rules. + for i, ingress := range spec.Ingress { + ingressPath := fldPath.Child("ingress").Index(i) + for i, port := range ingress.Ports { + portPath := ingressPath.Child("ports").Index(i) + if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP { + allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP)})) + } + if port.Port != nil { + if port.Port.Type == intstr.Int { + for _, msg := range validation.IsValidPortNum(int(port.Port.IntVal)) { + allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.IntVal, msg)) + } + } else { + for _, msg := range validation.IsValidPortName(port.Port.StrVal) { + allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.StrVal, msg)) + } + } + } + } + for i, from := range ingress.From { + fromPath := ingressPath.Child("from").Index(i) + numFroms := 0 + if from.PodSelector != nil { + numFroms++ + allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(from.PodSelector, fromPath.Child("podSelector"))...) + } + if from.NamespaceSelector != nil { + numFroms++ + allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(from.NamespaceSelector, fromPath.Child("namespaceSelector"))...) + } + + if numFroms == 0 { + allErrs = append(allErrs, field.Required(fromPath, "must specify a from type")) + } else if numFroms > 1 { + allErrs = append(allErrs, field.Forbidden(fromPath, "may not specify more than 1 from type")) + } + } + } + return allErrs +} + +// ValidateNetworkPolicy validates a networkpolicy. +func ValidateNetworkPolicy(np *networking.NetworkPolicy) field.ErrorList { + allErrs := apivalidation.ValidateObjectMeta(&np.ObjectMeta, true, ValidateNetworkPolicyName, field.NewPath("metadata")) + allErrs = append(allErrs, ValidateNetworkPolicySpec(&np.Spec, field.NewPath("spec"))...) + return allErrs +} + +// ValidateNetworkPolicyUpdate tests if an update to a NetworkPolicy is valid. +func ValidateNetworkPolicyUpdate(update, old *networking.NetworkPolicy) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...) + if !reflect.DeepEqual(update.Spec, old.Spec) { + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to networkpolicy spec are forbidden.")) + } + return allErrs +} diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go new file mode 100644 index 00000000000..6001d5bc4e6 --- /dev/null +++ b/pkg/apis/networking/validation/validation_test.go @@ -0,0 +1,346 @@ +/* +Copyright 2014 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 ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/networking" +) + +func TestValidateNetworkPolicy(t *testing.T) { + protocolTCP := api.ProtocolTCP + protocolUDP := api.ProtocolUDP + protocolICMP := api.Protocol("ICMP") + + successCases := []networking.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{}, + Ports: []networking.NetworkPolicyPort{}, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{ + { + Ports: []networking.NetworkPolicyPort{ + { + Protocol: nil, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 80}, + }, + { + Protocol: &protocolTCP, + Port: nil, + }, + { + Protocol: &protocolTCP, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 443}, + }, + { + Protocol: &protocolUDP, + Port: &intstr.IntOrString{Type: intstr.String, StrVal: "dns"}, + }, + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + } + + // Success cases are expected to pass validation. + for k, v := range successCases { + if errs := ValidateNetworkPolicy(&v); len(errs) != 0 { + t.Errorf("Expected success for %d, got %v", k, errs) + } + } + + invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} + errorCases := map[string]networking.NetworkPolicy{ + "namespaceSelector and podSelector": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + "invalid spec.podSelector": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: invalidSelector, + }, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"c": "d"}, + }, + }, + }, + }, + }, + }, + }, + "invalid ingress.ports.protocol": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{ + { + Ports: []networking.NetworkPolicyPort{ + { + Protocol: &protocolICMP, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 80}, + }, + }, + }, + }, + }, + }, + "invalid ingress.ports.port (int)": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{ + { + Ports: []networking.NetworkPolicyPort{ + { + Protocol: &protocolTCP, + Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 123456789}, + }, + }, + }, + }, + }, + }, + "invalid ingress.ports.port (str)": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{ + { + Ports: []networking.NetworkPolicyPort{ + { + Protocol: &protocolTCP, + Port: &intstr.IntOrString{Type: intstr.String, StrVal: "!@#$"}, + }, + }, + }, + }, + }, + }, + "invalid ingress.from.podSelector": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{ + { + PodSelector: &metav1.LabelSelector{ + MatchLabels: invalidSelector, + }, + }, + }, + }, + }, + }, + }, + "invalid ingress.from.namespaceSelector": { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{ + { + From: []networking.NetworkPolicyPeer{ + { + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: invalidSelector, + }, + }, + }, + }, + }, + }, + }, + } + + // Error cases are not expected to pass validation. + for testName, networkPolicy := range errorCases { + if errs := ValidateNetworkPolicy(&networkPolicy); len(errs) == 0 { + t.Errorf("Expected failure for test: %s", testName) + } + } +} + +func TestValidateNetworkPolicyUpdate(t *testing.T) { + type npUpdateTest struct { + old networking.NetworkPolicy + update networking.NetworkPolicy + } + successCases := []npUpdateTest{ + { + old: networking.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + update: networking.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + }, + } + + for _, successCase := range successCases { + successCase.old.ObjectMeta.ResourceVersion = "1" + successCase.update.ObjectMeta.ResourceVersion = "1" + if errs := ValidateNetworkPolicyUpdate(&successCase.update, &successCase.old); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + errorCases := map[string]npUpdateTest{ + "change name": { + old: networking.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + update: networking.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + }, + "change spec": { + old: networking.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + update: networking.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"}, + Spec: networking.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + Ingress: []networking.NetworkPolicyIngressRule{}, + }, + }, + }, + } + + for testName, errorCase := range errorCases { + if errs := ValidateNetworkPolicyUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 { + t.Errorf("expected failure: %s", testName) + } + } +} diff --git a/pkg/master/import_known_versions.go b/pkg/master/import_known_versions.go index 355b0f0beba..076b9889372 100644 --- a/pkg/master/import_known_versions.go +++ b/pkg/master/import_known_versions.go @@ -32,6 +32,7 @@ import ( _ "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/networking/install" _ "k8s.io/kubernetes/pkg/apis/policy/install" _ "k8s.io/kubernetes/pkg/apis/rbac/install" _ "k8s.io/kubernetes/pkg/apis/settings/install" diff --git a/pkg/master/master.go b/pkg/master/master.go index 33fbf4b0560..1c42ae1c79a 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -44,6 +44,7 @@ import ( batchapiv1 "k8s.io/kubernetes/pkg/apis/batch/v1" certificatesapiv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1" extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + networkingapiv1 "k8s.io/kubernetes/pkg/apis/networking/v1" policyapiv1beta1 "k8s.io/kubernetes/pkg/apis/policy/v1beta1" rbacv1alpha1 "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1" rbacv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1" @@ -71,6 +72,7 @@ import ( certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest" corerest "k8s.io/kubernetes/pkg/registry/core/rest" extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest" + networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest" policyrest "k8s.io/kubernetes/pkg/registry/policy/rest" rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest" settingsrest "k8s.io/kubernetes/pkg/registry/settings/rest" @@ -253,6 +255,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) batchrest.RESTStorageProvider{}, certificatesrest.RESTStorageProvider{}, extensionsrest.RESTStorageProvider{ResourceInterface: thirdparty.NewThirdPartyResourceServer(s, s.DiscoveryGroupManager, c.StorageFactory)}, + networkingrest.RESTStorageProvider{}, policyrest.RESTStorageProvider{}, rbacrest.RESTStorageProvider{Authorizer: c.GenericConfig.Authorizer}, settingsrest.RESTStorageProvider{}, @@ -390,6 +393,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { certificatesapiv1beta1.SchemeGroupVersion, authorizationapiv1.SchemeGroupVersion, authorizationapiv1beta1.SchemeGroupVersion, + networkingapiv1.SchemeGroupVersion, ) // all extensions resources except these are disabled by default diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index 16e8215d3e8..5fad424f90e 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -58,6 +58,7 @@ import ( "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" versionedextension "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/storage" @@ -132,7 +133,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer api.Kind("ConfigMap"): &ConfigMapDescriber{c}, extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, - extensions.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c}, + extensions.Kind("NetworkPolicy"): &ExtensionsNetworkPolicyDescriber{c}, autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, extensions.Kind("DaemonSet"): &DaemonSetDescriber{c}, extensions.Kind("Deployment"): &DeploymentDescriber{c, versionedClientsetForDeployment(c)}, @@ -146,6 +147,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer policy.Kind("PodDisruptionBudget"): &PodDisruptionBudgetDescriber{c}, rbac.Kind("RoleBinding"): &RoleBindingDescriber{c}, rbac.Kind("ClusterRoleBinding"): &ClusterRoleBindingDescriber{c}, + networking.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c}, } return m @@ -2812,13 +2814,41 @@ func describeCluster(cluster *federation.Cluster) (string, error) { }) } -// NetworkPolicyDescriber generates information about a NetworkPolicy +// ExtensionsNetworkPolicyDescriber generates information about an extensions.NetworkPolicy +type ExtensionsNetworkPolicyDescriber struct { + clientset.Interface +} + +func (d *ExtensionsNetworkPolicyDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { + c := d.Extensions().NetworkPolicies(namespace) + + networkPolicy, err := c.Get(name, metav1.GetOptions{}) + if err != nil { + return "", err + } + + return describeExtensionsNetworkPolicy(networkPolicy) +} + +func describeExtensionsNetworkPolicy(networkPolicy *extensions.NetworkPolicy) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%s\n", networkPolicy.Name) + w.Write(LEVEL_0, "Namespace:\t%s\n", networkPolicy.Namespace) + printLabelsMultiline(w, "Labels", networkPolicy.Labels) + printAnnotationsMultiline(w, "Annotations", networkPolicy.Annotations) + + return nil + }) +} + +// NetworkPolicyDescriber generates information about a networking.NetworkPolicy type NetworkPolicyDescriber struct { clientset.Interface } func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { - c := d.Extensions().NetworkPolicies(namespace) + c := d.Networking().NetworkPolicies(namespace) networkPolicy, err := c.Get(name, metav1.GetOptions{}) if err != nil { @@ -2828,7 +2858,7 @@ func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSetti return describeNetworkPolicy(networkPolicy) } -func describeNetworkPolicy(networkPolicy *extensions.NetworkPolicy) (string, error) { +func describeNetworkPolicy(networkPolicy *networking.NetworkPolicy) (string, error) { return tabbedString(func(out io.Writer) error { w := NewPrefixWriter(out) w.Write(LEVEL_0, "Name:\t%s\n", networkPolicy.Name) diff --git a/pkg/printers/internalversion/printers.go b/pkg/printers/internalversion/printers.go index 2f5228bda30..fdc141cdb6f 100644 --- a/pkg/printers/internalversion/printers.go +++ b/pkg/printers/internalversion/printers.go @@ -38,6 +38,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/networking" "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/settings" @@ -181,6 +182,8 @@ func AddHandlers(h *printers.HumanReadablePrinter) { h.Handler(thirdPartyResourceDataColumns, nil, printThirdPartyResourceDataList) h.Handler(clusterColumns, nil, printCluster) h.Handler(clusterColumns, nil, printClusterList) + h.Handler(networkPolicyColumns, nil, printExtensionsNetworkPolicy) + h.Handler(networkPolicyColumns, nil, printExtensionsNetworkPolicyList) h.Handler(networkPolicyColumns, nil, printNetworkPolicy) h.Handler(networkPolicyColumns, nil, printNetworkPolicyList) h.Handler(roleColumns, nil, printRole) @@ -1856,7 +1859,7 @@ func printPodSecurityPolicyList(list *extensions.PodSecurityPolicyList, w io.Wri return nil } -func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, options printers.PrintOptions) error { +func printExtensionsNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, options printers.PrintOptions) error { name := printers.FormatResourceName(options.Kind, networkPolicy.Name, options.WithKind) namespace := networkPolicy.Namespace @@ -1876,7 +1879,36 @@ func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, op return err } -func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, options printers.PrintOptions) error { +func printExtensionsNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, options printers.PrintOptions) error { + for i := range list.Items { + if err := printExtensionsNetworkPolicy(&list.Items[i], w, options); err != nil { + return err + } + } + return nil +} + +func printNetworkPolicy(networkPolicy *networking.NetworkPolicy, w io.Writer, options printers.PrintOptions) error { + name := printers.FormatResourceName(options.Kind, networkPolicy.Name, options.WithKind) + + namespace := networkPolicy.Namespace + + if options.WithNamespace { + if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { + return err + } + } + if _, err := fmt.Fprintf(w, "%s\t%v\t%s", name, metav1.FormatLabelSelector(&networkPolicy.Spec.PodSelector), translateTimestamp(networkPolicy.CreationTimestamp)); err != nil { + return err + } + if _, err := fmt.Fprint(w, printers.AppendLabels(networkPolicy.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err := fmt.Fprint(w, printers.AppendAllLabels(options.ShowLabels, networkPolicy.Labels)) + return err +} + +func printNetworkPolicyList(list *networking.NetworkPolicyList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printNetworkPolicy(&list.Items[i], w, options); err != nil { return err diff --git a/pkg/registry/networking/networkpolicy/doc.go b/pkg/registry/networking/networkpolicy/doc.go new file mode 100644 index 00000000000..7b338bc5e89 --- /dev/null +++ b/pkg/registry/networking/networkpolicy/doc.go @@ -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. +*/ + +package networkpolicy // import "k8s.io/kubernetes/pkg/registry/networking/networkpolicy" diff --git a/pkg/registry/networking/networkpolicy/registry.go b/pkg/registry/networking/networkpolicy/registry.go new file mode 100644 index 00000000000..36f1d3d5dbe --- /dev/null +++ b/pkg/registry/networking/networkpolicy/registry.go @@ -0,0 +1,84 @@ +/* +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 networkpolicy + +import ( + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/networking" +) + +// Registry is an interface for things that know how to store NetworkPolicies. +type Registry interface { + ListNetworkPolicies(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*networking.NetworkPolicyList, error) + CreateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error + UpdateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error + GetNetworkPolicy(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*networking.NetworkPolicy, error) + DeleteNetworkPolicy(ctx genericapirequest.Context, name string) error + WatchNetworkPolicies(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) +} + +// storage puts strong typing around storage calls +type storage struct { + rest.StandardStorage +} + +// NewRegistry returns a new Registry interface for the given Storage. Any mismatched +// types will panic. +func NewRegistry(s rest.StandardStorage) Registry { + return &storage{s} +} + +func (s *storage) ListNetworkPolicies(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*networking.NetworkPolicyList, error) { + obj, err := s.List(ctx, options) + if err != nil { + return nil, err + } + + return obj.(*networking.NetworkPolicyList), nil +} + +func (s *storage) CreateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error { + _, err := s.Create(ctx, np) + return err +} + +func (s *storage) UpdateNetworkPolicy(ctx genericapirequest.Context, np *networking.NetworkPolicy) error { + _, _, err := s.Update(ctx, np.Name, rest.DefaultUpdatedObjectInfo(np, api.Scheme)) + return err +} + +func (s *storage) WatchNetworkPolicies(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + return s.Watch(ctx, options) +} + +func (s *storage) GetNetworkPolicy(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (*networking.NetworkPolicy, error) { + obj, err := s.Get(ctx, name, options) + if err != nil { + return nil, err + } + return obj.(*networking.NetworkPolicy), nil +} + +func (s *storage) DeleteNetworkPolicy(ctx genericapirequest.Context, name string) error { + _, _, err := s.Delete(ctx, name, nil) + return err +} diff --git a/pkg/registry/networking/networkpolicy/storage/storage.go b/pkg/registry/networking/networkpolicy/storage/storage.go new file mode 100644 index 00000000000..e3693660348 --- /dev/null +++ b/pkg/registry/networking/networkpolicy/storage/storage.go @@ -0,0 +1,54 @@ +/* +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 storage + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/generic" + genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" + "k8s.io/kubernetes/pkg/api" + networkingapi "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/registry/cachesize" + "k8s.io/kubernetes/pkg/registry/networking/networkpolicy" +) + +// rest implements a RESTStorage for NetworkPolicies against etcd +type REST struct { + *genericregistry.Store +} + +// NewREST returns a RESTStorage object that will work against NetworkPolicies +func NewREST(optsGetter generic.RESTOptionsGetter) *REST { + store := &genericregistry.Store{ + Copier: api.Scheme, + NewFunc: func() runtime.Object { return &networkingapi.NetworkPolicy{} }, + NewListFunc: func() runtime.Object { return &networkingapi.NetworkPolicyList{} }, + PredicateFunc: networkpolicy.Matcher, + QualifiedResource: networkingapi.Resource("networkpolicies"), + WatchCacheSize: cachesize.GetWatchCacheSizeByResource("networkpolicies"), + + CreateStrategy: networkpolicy.Strategy, + UpdateStrategy: networkpolicy.Strategy, + DeleteStrategy: networkpolicy.Strategy, + } + options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: networkpolicy.GetAttrs} + if err := store.CompleteWithOptions(options); err != nil { + panic(err) // TODO: Propagate error up + } + + return &REST{store} +} diff --git a/pkg/registry/networking/networkpolicy/strategy.go b/pkg/registry/networking/networkpolicy/strategy.go new file mode 100644 index 00000000000..b6b027fb5ed --- /dev/null +++ b/pkg/registry/networking/networkpolicy/strategy.go @@ -0,0 +1,117 @@ +/* +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 networkpolicy + +import ( + "fmt" + "reflect" + + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/generic" + apistorage "k8s.io/apiserver/pkg/storage" + "k8s.io/apiserver/pkg/storage/names" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/apis/networking/validation" +) + +// networkPolicyStrategy implements verification logic for NetworkPolicies +type networkPolicyStrategy struct { + runtime.ObjectTyper + names.NameGenerator +} + +// Strategy is the default logic that applies when creating and updating NetworkPolicy objects. +var Strategy = networkPolicyStrategy{api.Scheme, names.SimpleNameGenerator} + +// NamespaceScoped returns true because all NetworkPolicies need to be within a namespace. +func (networkPolicyStrategy) NamespaceScoped() bool { + return true +} + +// PrepareForCreate clears the status of a NetworkPolicy before creation. +func (networkPolicyStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) { + networkPolicy := obj.(*networking.NetworkPolicy) + networkPolicy.Generation = 1 +} + +// PrepareForUpdate clears fields that are not allowed to be set by end users on update. +func (networkPolicyStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) { + newNetworkPolicy := obj.(*networking.NetworkPolicy) + oldNetworkPolicy := old.(*networking.NetworkPolicy) + + // Any changes to the spec increment the generation number, any changes to the + // status should reflect the generation number of the corresponding object. + // See metav1.ObjectMeta description for more information on Generation. + if !reflect.DeepEqual(oldNetworkPolicy.Spec, newNetworkPolicy.Spec) { + newNetworkPolicy.Generation = oldNetworkPolicy.Generation + 1 + } +} + +// Validate validates a new NetworkPolicy. +func (networkPolicyStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { + networkPolicy := obj.(*networking.NetworkPolicy) + return validation.ValidateNetworkPolicy(networkPolicy) +} + +// Canonicalize normalizes the object after validation. +func (networkPolicyStrategy) Canonicalize(obj runtime.Object) {} + +// AllowCreateOnUpdate is false for NetworkPolicy; this means POST is needed to create one. +func (networkPolicyStrategy) AllowCreateOnUpdate() bool { + return false +} + +// ValidateUpdate is the default update validation for an end user. +func (networkPolicyStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { + validationErrorList := validation.ValidateNetworkPolicy(obj.(*networking.NetworkPolicy)) + updateErrorList := validation.ValidateNetworkPolicyUpdate(obj.(*networking.NetworkPolicy), old.(*networking.NetworkPolicy)) + return append(validationErrorList, updateErrorList...) +} + +// AllowUnconditionalUpdate is the default update policy for NetworkPolicy objects. +func (networkPolicyStrategy) AllowUnconditionalUpdate() bool { + return true +} + +// SelectableFields returns a field set that represents the object. +func SelectableFields(networkPolicy *networking.NetworkPolicy) fields.Set { + return generic.ObjectMetaFieldsSet(&networkPolicy.ObjectMeta, true) +} + +// GetAttrs returns labels and fields of a given object for filtering purposes. +func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { + networkPolicy, ok := obj.(*networking.NetworkPolicy) + if !ok { + return nil, nil, fmt.Errorf("given object is not a NetworkPolicy.") + } + return labels.Set(networkPolicy.ObjectMeta.Labels), SelectableFields(networkPolicy), nil +} + +// Matcher is the filter used by the generic etcd backend to watch events +// from etcd to clients of the apiserver only interested in specific labels/fields. +func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { + return apistorage.SelectionPredicate{ + Label: label, + Field: field, + GetAttrs: GetAttrs, + } +} diff --git a/pkg/registry/networking/rest/storage_settings.go b/pkg/registry/networking/rest/storage_settings.go new file mode 100644 index 00000000000..c662025dd22 --- /dev/null +++ b/pkg/registry/networking/rest/storage_settings.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 rest + +import ( + "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" + genericapiserver "k8s.io/apiserver/pkg/server" + serverstorage "k8s.io/apiserver/pkg/server/storage" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/networking" + networkingapiv1 "k8s.io/kubernetes/pkg/apis/networking/v1" + networkpolicystore "k8s.io/kubernetes/pkg/registry/networking/networkpolicy/storage" +) + +type RESTStorageProvider struct{} + +func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool) { + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(networking.GroupName, api.Registry, api.Scheme, api.ParameterCodec, api.Codecs) + + if apiResourceConfigSource.AnyResourcesForVersionEnabled(networkingapiv1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[networkingapiv1.SchemeGroupVersion.Version] = p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter) + apiGroupInfo.GroupMeta.GroupVersion = networkingapiv1.SchemeGroupVersion + } + + return apiGroupInfo, true +} + +func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage { + version := networkingapiv1.SchemeGroupVersion + + storage := map[string]rest.Storage{} + if apiResourceConfigSource.ResourceEnabled(version.WithResource("networkpolicies")) { + networkPolicyStorage := networkpolicystore.NewREST(restOptionsGetter) + storage["networkpolicies"] = networkPolicyStorage + } + return storage +} + +func (p RESTStorageProvider) GroupName() string { + return networking.GroupName +} diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index 5ab5b3355cd..e2712ab98bb 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -219,6 +219,14 @@ var etcdStorageData = map[schema.GroupVersionResource]struct { }, // -- + // k8s.io/kubernetes/pkg/apis/networking/v1 + gvr("networking.k8s.io", "v1", "networkpolicies"): { + stub: `{"metadata": {"name": "np2"}, "spec": {"podSelector": {"matchLabels": {"e": "f"}}}}`, + expectedEtcdPath: "/registry/networkpolicies/etcdstoragepathtestnamespace/np2", + expectedGVK: gvkP("extensions", "v1beta1", "NetworkPolicy"), + }, + // -- + // k8s.io/kubernetes/pkg/apis/policy/v1beta1 gvr("policy", "v1beta1", "poddisruptionbudgets"): { stub: `{"metadata": {"name": "pdb1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`,