diff --git a/api/swagger-spec/oapi-v1.json b/api/swagger-spec/oapi-v1.json index cdc74293c147..1172fe08b113 100644 --- a/api/swagger-spec/oapi-v1.json +++ b/api/swagger-spec/oapi-v1.json @@ -6677,6 +6677,827 @@ } ] }, + { + "path": "/oapi/v1/namespaces/{namespace}/egressnetworkpolicies", + "description": "OpenShift REST API, version v1", + "operations": [ + { + "type": "v1.EgressNetworkPolicyList", + "method": "GET", + "summary": "list or watch objects of kind EgressNetworkPolicy", + "nickname": "listNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicyList" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.EgressNetworkPolicy", + "method": "POST", + "summary": "create a EgressNetworkPolicy", + "nickname": "createNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.EgressNetworkPolicy", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicy" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "unversioned.Status", + "method": "DELETE", + "summary": "delete collection of EgressNetworkPolicy", + "nickname": "deletecollectionNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "unversioned.Status" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/oapi/v1/watch/namespaces/{namespace}/egressnetworkpolicies", + "description": "OpenShift REST API, version v1", + "operations": [ + { + "type": "*versioned.Event", + "method": "GET", + "summary": "watch individual changes to a list of EgressNetworkPolicy", + "nickname": "watchNamespacedEgressNetworkPolicyList", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "*versioned.Event" + } + ], + "produces": [ + "application/json", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/oapi/v1/namespaces/{namespace}/egressnetworkpolicies/{name}", + "description": "OpenShift REST API, version v1", + "operations": [ + { + "type": "v1.EgressNetworkPolicy", + "method": "GET", + "summary": "read the specified EgressNetworkPolicy", + "nickname": "readNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "export", + "description": "Should this value be exported. Export strips fields that a user can not specify.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "exact", + "description": "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the EgressNetworkPolicy", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicy" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.EgressNetworkPolicy", + "method": "PUT", + "summary": "replace the specified EgressNetworkPolicy", + "nickname": "replaceNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.EgressNetworkPolicy", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the EgressNetworkPolicy", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicy" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.EgressNetworkPolicy", + "method": "PATCH", + "summary": "partially update the specified EgressNetworkPolicy", + "nickname": "patchNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "unversioned.Patch", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the EgressNetworkPolicy", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicy" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json" + ] + }, + { + "type": "unversioned.Status", + "method": "DELETE", + "summary": "delete a EgressNetworkPolicy", + "nickname": "deleteNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.DeleteOptions", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the EgressNetworkPolicy", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "unversioned.Status" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/oapi/v1/watch/namespaces/{namespace}/egressnetworkpolicies/{name}", + "description": "OpenShift REST API, version v1", + "operations": [ + { + "type": "*versioned.Event", + "method": "GET", + "summary": "watch changes to an object of kind EgressNetworkPolicy", + "nickname": "watchNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the EgressNetworkPolicy", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "*versioned.Event" + } + ], + "produces": [ + "application/json", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/oapi/v1/egressnetworkpolicies", + "description": "OpenShift REST API, version v1", + "operations": [ + { + "type": "v1.EgressNetworkPolicyList", + "method": "GET", + "summary": "list or watch objects of kind EgressNetworkPolicy", + "nickname": "listNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicyList" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1.EgressNetworkPolicy", + "method": "POST", + "summary": "create a EgressNetworkPolicy", + "nickname": "createNamespacedEgressNetworkPolicy", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.EgressNetworkPolicy", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1.EgressNetworkPolicy" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + } + ] + }, + { + "path": "/oapi/v1/watch/egressnetworkpolicies", + "description": "OpenShift REST API, version v1", + "operations": [ + { + "type": "*versioned.Event", + "method": "GET", + "summary": "watch individual changes to a list of EgressNetworkPolicy", + "nickname": "watchNamespacedEgressNetworkPolicyList", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "labelSelector", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "fieldSelector", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "required": false, + "allowMultiple": false + }, + { + "type": "boolean", + "paramType": "query", + "name": "watch", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "query", + "name": "resourceVersion", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history.", + "required": false, + "allowMultiple": false + }, + { + "type": "integer", + "paramType": "query", + "name": "timeoutSeconds", + "description": "Timeout for the list/watch call.", + "required": false, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "*versioned.Event" + } + ], + "produces": [ + "application/json", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "consumes": [ + "*/*" + ] + } + ] + }, { "path": "/oapi/v1/namespaces/{namespace}/generatedeploymentconfigs/{name}", "description": "OpenShift REST API, version v1", @@ -23364,6 +24185,106 @@ } } }, + "v1.EgressNetworkPolicyList": { + "id": "v1.EgressNetworkPolicyList", + "description": "EgressNetworkPolicyList is a collection of EgressNetworkPolicy", + "required": [ + "items" + ], + "properties": { + "kind": { + "type": "string", + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/release-1.3/docs/devel/api-conventions.md#types-kinds" + }, + "apiVersion": { + "type": "string", + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/release-1.3/docs/devel/api-conventions.md#resources" + }, + "metadata": { + "$ref": "unversioned.ListMeta", + "description": "metadata for EgressNetworkPolicyList" + }, + "items": { + "type": "array", + "items": { + "$ref": "v1.EgressNetworkPolicy" + }, + "description": "items is the list of policies" + } + } + }, + "v1.EgressNetworkPolicy": { + "id": "v1.EgressNetworkPolicy", + "description": "EgressNetworkPolicy describes the current egress network policy for a Namespace. When using the 'redhat/openshift-ovs-multitenant' network plugin, traffic from a pod to an IP address outside the cluster will be checked against each EgressNetworkPolicyRule in the pod's namespace's EgressNetworkPolicy, in order. If no rule matches (or no EgressNetworkPolicy is present) then the traffic will be allowed by default.", + "required": [ + "spec" + ], + "properties": { + "kind": { + "type": "string", + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/release-1.3/docs/devel/api-conventions.md#types-kinds" + }, + "apiVersion": { + "type": "string", + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/release-1.3/docs/devel/api-conventions.md#resources" + }, + "metadata": { + "$ref": "v1.ObjectMeta", + "description": "metadata for EgressNetworkPolicy" + }, + "spec": { + "$ref": "v1.EgressNetworkPolicySpec", + "description": "spec is the specification of the current egress network policy" + } + } + }, + "v1.EgressNetworkPolicySpec": { + "id": "v1.EgressNetworkPolicySpec", + "description": "EgressNetworkPolicySpec provides a list of policies on outgoing network traffic", + "required": [ + "egress" + ], + "properties": { + "egress": { + "type": "array", + "items": { + "$ref": "v1.EgressNetworkPolicyRule" + }, + "description": "egress contains the list of egress policy rules" + } + } + }, + "v1.EgressNetworkPolicyRule": { + "id": "v1.EgressNetworkPolicyRule", + "description": "EgressNetworkPolicyRule contains a single egress network policy rule", + "required": [ + "type", + "to" + ], + "properties": { + "type": { + "type": "string", + "description": "type marks this as an \"Allow\" or \"Deny\" rule" + }, + "to": { + "$ref": "v1.EgressNetworkPolicyPeer", + "description": "to is the target that traffic is allowed/denied to" + } + } + }, + "v1.EgressNetworkPolicyPeer": { + "id": "v1.EgressNetworkPolicyPeer", + "description": "EgressNetworkPolicyPeer specifies a target to apply egress network policy to", + "required": [ + "cidrSelector" + ], + "properties": { + "cidrSelector": { + "type": "string", + "description": "cidrSelector is the CIDR range to allow/deny traffic to" + } + } + }, "v1.GroupList": { "id": "v1.GroupList", "description": "GroupList is a collection of Groups", diff --git a/contrib/completions/bash/oadm b/contrib/completions/bash/oadm index bfb6db4a3dff..9b11f772e260 100644 --- a/contrib/completions/bash/oadm +++ b/contrib/completions/bash/oadm @@ -2430,6 +2430,7 @@ _oadm_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") diff --git a/contrib/completions/bash/oc b/contrib/completions/bash/oc index 33f4fe6d54a0..2978f3954c01 100644 --- a/contrib/completions/bash/oc +++ b/contrib/completions/bash/oc @@ -1582,6 +1582,7 @@ _oc_get() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -1650,6 +1651,7 @@ _oc_get() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -1778,6 +1780,7 @@ _oc_describe() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("group") must_have_one_noun+=("horizontalpodautoscaler") @@ -1928,6 +1931,7 @@ _oc_edit() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -1996,6 +2000,7 @@ _oc_edit() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -2614,6 +2619,7 @@ _oc_label() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -2682,6 +2688,7 @@ _oc_label() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -3020,6 +3027,7 @@ _oc_delete() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -3088,6 +3096,7 @@ _oc_delete() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -6513,6 +6522,7 @@ _oc_adm_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -9384,6 +9394,7 @@ _oc_patch() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -9452,6 +9463,7 @@ _oc_patch() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") diff --git a/contrib/completions/bash/openshift b/contrib/completions/bash/openshift index dc14a7072ca3..bc63f7f7efe0 100644 --- a/contrib/completions/bash/openshift +++ b/contrib/completions/bash/openshift @@ -3078,6 +3078,7 @@ _openshift_admin_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -5921,6 +5922,7 @@ _openshift_cli_get() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -5989,6 +5991,7 @@ _openshift_cli_get() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -6118,6 +6121,7 @@ _openshift_cli_describe() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("group") must_have_one_noun+=("horizontalpodautoscaler") @@ -6269,6 +6273,7 @@ _openshift_cli_edit() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -6337,6 +6342,7 @@ _openshift_cli_edit() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -6963,6 +6969,7 @@ _openshift_cli_label() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -7031,6 +7038,7 @@ _openshift_cli_label() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -7372,6 +7380,7 @@ _openshift_cli_delete() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -7440,6 +7449,7 @@ _openshift_cli_delete() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -10930,6 +10940,7 @@ _openshift_cli_adm_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -13856,6 +13867,7 @@ _openshift_cli_patch() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -13924,6 +13936,7 @@ _openshift_cli_patch() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -15846,6 +15859,7 @@ _openshift_kube_get() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -15914,6 +15928,7 @@ _openshift_kube_get() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -17438,6 +17453,7 @@ _openshift_kube_patch() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -17506,6 +17522,7 @@ _openshift_kube_patch() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -17685,6 +17702,7 @@ _openshift_kube_delete() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -17753,6 +17771,7 @@ _openshift_kube_delete() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -17928,6 +17947,7 @@ _openshift_kube_edit() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -17996,6 +18016,7 @@ _openshift_kube_edit() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -20168,6 +20189,7 @@ _openshift_kube_label() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -20236,6 +20258,7 @@ _openshift_kube_label() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -20558,6 +20581,7 @@ _openshift_kube_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") diff --git a/contrib/completions/zsh/oadm b/contrib/completions/zsh/oadm index 442b9f4fe467..5011577dbeff 100644 --- a/contrib/completions/zsh/oadm +++ b/contrib/completions/zsh/oadm @@ -2591,6 +2591,7 @@ _oadm_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") diff --git a/contrib/completions/zsh/oc b/contrib/completions/zsh/oc index 8a681eab7a34..5fe41012aacf 100644 --- a/contrib/completions/zsh/oc +++ b/contrib/completions/zsh/oc @@ -1743,6 +1743,7 @@ _oc_get() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -1811,6 +1812,7 @@ _oc_get() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -1939,6 +1941,7 @@ _oc_describe() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("group") must_have_one_noun+=("horizontalpodautoscaler") @@ -2089,6 +2092,7 @@ _oc_edit() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -2157,6 +2161,7 @@ _oc_edit() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -2775,6 +2780,7 @@ _oc_label() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -2843,6 +2849,7 @@ _oc_label() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -3181,6 +3188,7 @@ _oc_delete() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -3249,6 +3257,7 @@ _oc_delete() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -6674,6 +6683,7 @@ _oc_adm_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -9545,6 +9555,7 @@ _oc_patch() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -9613,6 +9624,7 @@ _oc_patch() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") diff --git a/contrib/completions/zsh/openshift b/contrib/completions/zsh/openshift index 41d3a6837788..664fa9e3088e 100644 --- a/contrib/completions/zsh/openshift +++ b/contrib/completions/zsh/openshift @@ -3239,6 +3239,7 @@ _openshift_admin_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -6082,6 +6083,7 @@ _openshift_cli_get() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -6150,6 +6152,7 @@ _openshift_cli_get() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -6279,6 +6282,7 @@ _openshift_cli_describe() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("group") must_have_one_noun+=("horizontalpodautoscaler") @@ -6430,6 +6434,7 @@ _openshift_cli_edit() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -6498,6 +6503,7 @@ _openshift_cli_edit() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -7124,6 +7130,7 @@ _openshift_cli_label() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -7192,6 +7199,7 @@ _openshift_cli_label() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -7533,6 +7541,7 @@ _openshift_cli_delete() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -7601,6 +7610,7 @@ _openshift_cli_delete() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -11091,6 +11101,7 @@ _openshift_cli_adm_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -14017,6 +14028,7 @@ _openshift_cli_patch() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -14085,6 +14097,7 @@ _openshift_cli_patch() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -16007,6 +16020,7 @@ _openshift_kube_get() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -16075,6 +16089,7 @@ _openshift_kube_get() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -17599,6 +17614,7 @@ _openshift_kube_patch() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -17667,6 +17683,7 @@ _openshift_kube_patch() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -17846,6 +17863,7 @@ _openshift_kube_delete() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -17914,6 +17932,7 @@ _openshift_kube_delete() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -18089,6 +18108,7 @@ _openshift_kube_edit() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -18157,6 +18177,7 @@ _openshift_kube_edit() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -20329,6 +20350,7 @@ _openshift_kube_label() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") @@ -20397,6 +20419,7 @@ _openshift_kube_label() noun_aliases+=("deploymentconfigs") noun_aliases+=("deployments") noun_aliases+=("ds") + noun_aliases+=("egressnetworkpolicies") noun_aliases+=("endpoints") noun_aliases+=("ep") noun_aliases+=("ev") @@ -20719,6 +20742,7 @@ _openshift_kube_taint() must_have_one_noun+=("daemonset") must_have_one_noun+=("deployment") must_have_one_noun+=("deploymentconfig") + must_have_one_noun+=("egressnetworkpolicy") must_have_one_noun+=("endpoints") must_have_one_noun+=("event") must_have_one_noun+=("group") diff --git a/pkg/api/validation/register.go b/pkg/api/validation/register.go index 79f7ab6db146..90bd26a1b7e1 100644 --- a/pkg/api/validation/register.go +++ b/pkg/api/validation/register.go @@ -86,6 +86,7 @@ func registerAll() { Validator.MustRegister(&sdnapi.ClusterNetwork{}, sdnvalidation.ValidateClusterNetwork, sdnvalidation.ValidateClusterNetworkUpdate) Validator.MustRegister(&sdnapi.HostSubnet{}, sdnvalidation.ValidateHostSubnet, sdnvalidation.ValidateHostSubnetUpdate) Validator.MustRegister(&sdnapi.NetNamespace{}, sdnvalidation.ValidateNetNamespace, sdnvalidation.ValidateNetNamespaceUpdate) + Validator.MustRegister(&sdnapi.EgressNetworkPolicy{}, sdnvalidation.ValidateEgressNetworkPolicy, sdnvalidation.ValidateEgressNetworkPolicyUpdate) Validator.MustRegister(&templateapi.Template{}, templatevalidation.ValidateTemplate, templatevalidation.ValidateTemplateUpdate) diff --git a/pkg/client/client.go b/pkg/client/client.go index 7600bcfd2b26..c5c00a17f0d0 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -34,6 +34,7 @@ type Interface interface { HostSubnetsInterface NetNamespacesInterface ClusterNetworkingInterface + EgressNetworkPoliciesNamespacer IdentitiesInterface UsersInterface GroupsInterface @@ -145,6 +146,11 @@ func (c *Client) ClusterNetwork() ClusterNetworkInterface { return newClusterNetwork(c) } +// EgressNetworkPolicies provides a REST client for EgressNetworkPolicy +func (c *Client) EgressNetworkPolicies(namespace string) EgressNetworkPolicyInterface { + return newEgressNetworkPolicy(c, namespace) +} + // Users provides a REST client for User func (c *Client) Users() UserInterface { return newUsers(c) diff --git a/pkg/client/egressnetworkpolicy.go b/pkg/client/egressnetworkpolicy.go new file mode 100644 index 000000000000..17a88be3428c --- /dev/null +++ b/pkg/client/egressnetworkpolicy.go @@ -0,0 +1,85 @@ +package client + +import ( + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/watch" + + sdnapi "github.com/openshift/origin/pkg/sdn/api" +) + +// EgressNetworkPoliciesNamespacer has methods to work with EgressNetworkPolicy resources in a namespace +type EgressNetworkPoliciesNamespacer interface { + EgressNetworkPolicies(namespace string) EgressNetworkPolicyInterface +} + +// EgressNetworkPolicyInterface exposes methods on egressNetworkPolicy resources. +type EgressNetworkPolicyInterface interface { + List(opts kapi.ListOptions) (*sdnapi.EgressNetworkPolicyList, error) + Get(name string) (*sdnapi.EgressNetworkPolicy, error) + Create(sub *sdnapi.EgressNetworkPolicy) (*sdnapi.EgressNetworkPolicy, error) + Update(sub *sdnapi.EgressNetworkPolicy) (*sdnapi.EgressNetworkPolicy, error) + Delete(name string) error + Watch(opts kapi.ListOptions) (watch.Interface, error) +} + +// egressNetworkPolicy implements EgressNetworkPolicyInterface interface +type egressNetworkPolicy struct { + r *Client + ns string +} + +// newEgressNetworkPolicy returns a egressNetworkPolicy +func newEgressNetworkPolicy(c *Client, namespace string) *egressNetworkPolicy { + return &egressNetworkPolicy{ + r: c, + ns: namespace, + } +} + +// List returns a list of EgressNetworkPolicy that match the label and field selectors. +func (c *egressNetworkPolicy) List(opts kapi.ListOptions) (result *sdnapi.EgressNetworkPolicyList, err error) { + result = &sdnapi.EgressNetworkPolicyList{} + err = c.r.Get(). + Namespace(c.ns). + Resource("egressNetworkPolicies"). + VersionedParams(&opts, kapi.ParameterCodec). + Do(). + Into(result) + return +} + +// Get returns information about a particular firewall +func (c *egressNetworkPolicy) Get(name string) (result *sdnapi.EgressNetworkPolicy, err error) { + result = &sdnapi.EgressNetworkPolicy{} + err = c.r.Get().Namespace(c.ns).Resource("egressNetworkPolicies").Name(name).Do().Into(result) + return +} + +// Create creates a new EgressNetworkPolicy. Returns the server's representation of EgressNetworkPolicy and error if one occurs. +func (c *egressNetworkPolicy) Create(fw *sdnapi.EgressNetworkPolicy) (result *sdnapi.EgressNetworkPolicy, err error) { + result = &sdnapi.EgressNetworkPolicy{} + err = c.r.Post().Namespace(c.ns).Resource("egressNetworkPolicies").Body(fw).Do().Into(result) + return +} + +// Update updates the EgressNetworkPolicy on the server. Returns the server's representation of the EgressNetworkPolicy and error if one occurs. +func (c *egressNetworkPolicy) Update(fw *sdnapi.EgressNetworkPolicy) (result *sdnapi.EgressNetworkPolicy, err error) { + result = &sdnapi.EgressNetworkPolicy{} + err = c.r.Put().Namespace(c.ns).Resource("egressNetworkPolicies").Name(fw.Name).Body(fw).Do().Into(result) + return +} + +// Delete takes the name of the EgressNetworkPolicy, and returns an error if one occurs during deletion of the EgressNetworkPolicy +func (c *egressNetworkPolicy) Delete(name string) error { + return c.r.Delete().Namespace(c.ns).Resource("egressNetworkPolicies").Name(name).Do().Error() +} + +// Watch returns a watch.Interface that watches the requested EgressNetworkPolicies +func (c *egressNetworkPolicy) Watch(opts kapi.ListOptions) (watch.Interface, error) { + return c.r.Get(). + Prefix("watch"). + Namespace(c.ns). + Resource("egressNetworkPolicies"). + VersionedParams(&opts, kapi.ParameterCodec). + Watch() +} diff --git a/pkg/client/testclient/fake.go b/pkg/client/testclient/fake.go index 4fe7c41bff29..0a0397ce241a 100644 --- a/pkg/client/testclient/fake.go +++ b/pkg/client/testclient/fake.go @@ -201,6 +201,11 @@ func (c *Fake) ClusterNetwork() client.ClusterNetworkInterface { return &FakeClusterNetwork{Fake: c} } +// EgressNetworkPolicies provides a fake REST client for EgressNetworkPolicies +func (c *Fake) EgressNetworkPolicies(namespace string) client.EgressNetworkPolicyInterface { + return &FakeEgressNetworkPolicy{Fake: c, Namespace: namespace} +} + // Templates provides a fake REST client for Templates func (c *Fake) Templates(namespace string) client.TemplateInterface { return &FakeTemplates{Fake: c, Namespace: namespace} diff --git a/pkg/client/testclient/fake_egressnetworkpolicy.go b/pkg/client/testclient/fake_egressnetworkpolicy.go new file mode 100644 index 000000000000..0df0fcb9cdc0 --- /dev/null +++ b/pkg/client/testclient/fake_egressnetworkpolicy.go @@ -0,0 +1,61 @@ +package testclient + +import ( + kapi "k8s.io/kubernetes/pkg/api" + ktestclient "k8s.io/kubernetes/pkg/client/unversioned/testclient" + "k8s.io/kubernetes/pkg/watch" + + sdnapi "github.com/openshift/origin/pkg/sdn/api" +) + +// FakeEgressNetworkPolicy implements EgressNetworkPolicyInterface. Meant to be embedded into a struct to get a default +// implementation. This makes faking out just the methods you want to test easier. +type FakeEgressNetworkPolicy struct { + Fake *Fake + Namespace string +} + +func (c *FakeEgressNetworkPolicy) Get(name string) (*sdnapi.EgressNetworkPolicy, error) { + obj, err := c.Fake.Invokes(ktestclient.NewGetAction("egressnetworkpolicies", c.Namespace, name), &sdnapi.EgressNetworkPolicy{}) + if obj == nil { + return nil, err + } + + return obj.(*sdnapi.EgressNetworkPolicy), err +} + +func (c *FakeEgressNetworkPolicy) List(opts kapi.ListOptions) (*sdnapi.EgressNetworkPolicyList, error) { + obj, err := c.Fake.Invokes(ktestclient.NewListAction("egressnetworkpolicies", c.Namespace, opts), &sdnapi.EgressNetworkPolicyList{}) + if obj == nil { + return nil, err + } + + return obj.(*sdnapi.EgressNetworkPolicyList), err +} + +func (c *FakeEgressNetworkPolicy) Create(inObj *sdnapi.EgressNetworkPolicy) (*sdnapi.EgressNetworkPolicy, error) { + obj, err := c.Fake.Invokes(ktestclient.NewCreateAction("egressnetworkpolicies", c.Namespace, inObj), inObj) + if obj == nil { + return nil, err + } + + return obj.(*sdnapi.EgressNetworkPolicy), err +} + +func (c *FakeEgressNetworkPolicy) Update(inObj *sdnapi.EgressNetworkPolicy) (*sdnapi.EgressNetworkPolicy, error) { + obj, err := c.Fake.Invokes(ktestclient.NewUpdateAction("egressnetworkpolicies", c.Namespace, inObj), inObj) + if obj == nil { + return nil, err + } + + return obj.(*sdnapi.EgressNetworkPolicy), err +} + +func (c *FakeEgressNetworkPolicy) Delete(name string) error { + _, err := c.Fake.Invokes(ktestclient.NewDeleteAction("egressnetworkpolicies", c.Namespace, name), &sdnapi.EgressNetworkPolicy{}) + return err +} + +func (c *FakeEgressNetworkPolicy) Watch(opts kapi.ListOptions) (watch.Interface, error) { + return c.Fake.InvokesWatch(ktestclient.NewWatchAction("egressnetworkpolicies", c.Namespace, opts)) +} diff --git a/pkg/cmd/cli/describe/describer.go b/pkg/cmd/cli/describe/describer.go index 4439cb00c52b..c211701afb01 100644 --- a/pkg/cmd/cli/describe/describer.go +++ b/pkg/cmd/cli/describe/describer.go @@ -31,6 +31,7 @@ import ( projectapi "github.com/openshift/origin/pkg/project/api" quotaapi "github.com/openshift/origin/pkg/quota/api" routeapi "github.com/openshift/origin/pkg/route/api" + sdnapi "github.com/openshift/origin/pkg/sdn/api" templateapi "github.com/openshift/origin/pkg/template/api" userapi "github.com/openshift/origin/pkg/user/api" ) @@ -62,6 +63,7 @@ func describerMap(c *client.Client, kclient kclient.Interface, host string) map[ userapi.Kind("UserIdentityMapping"): &UserIdentityMappingDescriber{c}, quotaapi.Kind("ClusterResourceQuota"): &ClusterQuotaDescriber{c}, quotaapi.Kind("AppliedClusterResourceQuota"): &AppliedClusterQuotaDescriber{c}, + sdnapi.Kind("EgressNetworkPolicy"): &EgressNetworkPolicyDescriber{c}, } return m } @@ -1486,3 +1488,23 @@ func (d *AppliedClusterQuotaDescriber) Describe(namespace, name string, settings } return DescribeClusterQuota(quotaapi.ConvertAppliedClusterResourceQuotaToClusterResourceQuota(quota)) } + +type EgressNetworkPolicyDescriber struct { + osClient client.Interface +} + +// Describe returns the description of an EgressNetworkPolicy +func (d *EgressNetworkPolicyDescriber) Describe(namespace, name string, settings kctl.DescriberSettings) (string, error) { + c := d.osClient.EgressNetworkPolicies(namespace) + policy, err := c.Get(name) + if err != nil { + return "", err + } + return tabbedString(func(out *tabwriter.Writer) error { + formatMeta(out, policy.ObjectMeta) + for _, rule := range policy.Spec.Egress { + fmt.Fprintf(out, "Rule:\t%s to %s\n", rule.Type, rule.To.CIDRSelector) + } + return nil + }) +} diff --git a/pkg/cmd/cli/describe/printer.go b/pkg/cmd/cli/describe/printer.go index f96e6d0491ea..6133dfe4643c 100644 --- a/pkg/cmd/cli/describe/printer.go +++ b/pkg/cmd/cli/describe/printer.go @@ -57,9 +57,10 @@ var ( // IsPersonalSubjectAccessReviewColumns contains known custom role extensions IsPersonalSubjectAccessReviewColumns = []string{"NAME"} - hostSubnetColumns = []string{"NAME", "HOST", "HOST IP", "SUBNET"} - netNamespaceColumns = []string{"NAME", "NETID"} - clusterNetworkColumns = []string{"NAME", "NETWORK", "HOST SUBNET LENGTH", "SERVICE NETWORK", "PLUGIN NAME"} + hostSubnetColumns = []string{"NAME", "HOST", "HOST IP", "SUBNET"} + netNamespaceColumns = []string{"NAME", "NETID"} + clusterNetworkColumns = []string{"NAME", "NETWORK", "HOST SUBNET LENGTH", "SERVICE NETWORK", "PLUGIN NAME"} + egressNetworkPolicyColumns = []string{"NAME"} clusterResourceQuotaColumns = []string{"NAME", "LABEL SELECTOR", "ANNOTATION SELECTOR"} ) @@ -131,6 +132,8 @@ func NewHumanReadablePrinter(printOptions *kctl.PrintOptions) *kctl.HumanReadabl p.Handler(netNamespaceColumns, printNetNamespace) p.Handler(clusterNetworkColumns, printClusterNetwork) p.Handler(clusterNetworkColumns, printClusterNetworkList) + p.Handler(egressNetworkPolicyColumns, printEgressNetworkPolicy) + p.Handler(egressNetworkPolicyColumns, printEgressNetworkPolicyList) p.Handler(clusterResourceQuotaColumns, printClusterResourceQuota) p.Handler(clusterResourceQuotaColumns, printClusterResourceQuotaList) @@ -953,6 +956,27 @@ func printClusterNetworkList(list *sdnapi.ClusterNetworkList, w io.Writer, opts return nil } +func printEgressNetworkPolicy(n *sdnapi.EgressNetworkPolicy, w io.Writer, opts kctl.PrintOptions) error { + if opts.WithNamespace { + if _, err := fmt.Fprintf(w, "%s\t", n.Namespace); err != nil { + return err + } + } + if _, err := fmt.Fprintf(w, "%s\n", n.Name); err != nil { + return err + } + return nil +} + +func printEgressNetworkPolicyList(list *sdnapi.EgressNetworkPolicyList, w io.Writer, opts kctl.PrintOptions) error { + for _, item := range list.Items { + if err := printEgressNetworkPolicy(&item, w, opts); err != nil { + return err + } + } + return nil +} + func appendItemLabels(itemLabels map[string]string, w io.Writer, columnLabels []string, showLabels bool) error { if _, err := fmt.Fprint(w, kctl.AppendLabels(itemLabels, columnLabels)); err != nil { return err diff --git a/pkg/cmd/server/bootstrappolicy/policy.go b/pkg/cmd/server/bootstrappolicy/policy.go index 3c1c5496368d..8b598f236c50 100644 --- a/pkg/cmd/server/bootstrappolicy/policy.go +++ b/pkg/cmd/server/bootstrappolicy/policy.go @@ -105,7 +105,7 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole { Name: ClusterReaderRoleName, }, Rules: []authorizationapi.PolicyRule{ - authorizationapi.NewRule(read...).Groups(kapiGroup).Resources("bindings", "componentstatuses", "configmaps", "endpoints", "events", "limitranges", + authorizationapi.NewRule(read...).Groups(kapiGroup).Resources("bindings", "componentstatuses", "configmaps", "egressnetworkpolicies", "endpoints", "events", "limitranges", "namespaces", "namespaces/status", "nodes", "nodes/status", "persistentvolumeclaims", "persistentvolumeclaims/status", "persistentvolumes", "persistentvolumes/status", "pods", "pods/binding", "pods/log", "pods/status", "podtemplates", "replicationcontrollers", "replicationcontrollers/scale", "replicationcontrollers/status", "resourcequotas", "resourcequotas/status", "securitycontextconstraints", "serviceaccounts", "services", @@ -598,6 +598,7 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole { Rules: []authorizationapi.PolicyRule{ authorizationapi.NewRule(read...).Groups(sdnGroup).Resources("hostsubnets", "netnamespaces").RuleOrDie(), authorizationapi.NewRule(read...).Groups(kapiGroup).Resources("nodes", "namespaces").RuleOrDie(), + authorizationapi.NewRule(read...).Groups(kapiGroup).Resources("egressnetworkpolicies").RuleOrDie(), authorizationapi.NewRule("get").Groups(sdnGroup).Resources("clusternetworks").RuleOrDie(), }, diff --git a/pkg/cmd/server/origin/master.go b/pkg/cmd/server/origin/master.go index 6352a87f9c49..43a5b4bd8555 100644 --- a/pkg/cmd/server/origin/master.go +++ b/pkg/cmd/server/origin/master.go @@ -76,6 +76,7 @@ import ( routeallocationcontroller "github.com/openshift/origin/pkg/route/controller/allocation" routeetcd "github.com/openshift/origin/pkg/route/registry/route/etcd" clusternetworketcd "github.com/openshift/origin/pkg/sdn/registry/clusternetwork/etcd" + egressnetworkpolicyetcd "github.com/openshift/origin/pkg/sdn/registry/egressnetworkpolicy/etcd" hostsubnetetcd "github.com/openshift/origin/pkg/sdn/registry/hostsubnet/etcd" netnamespaceetcd "github.com/openshift/origin/pkg/sdn/registry/netnamespace/etcd" "github.com/openshift/origin/pkg/service" @@ -426,6 +427,8 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage { checkStorageErr(err) clusterNetworkStorage, err := clusternetworketcd.NewREST(c.RESTOptionsGetter) checkStorageErr(err) + egressNetworkPolicyStorage, err := egressnetworkpolicyetcd.NewREST(c.RESTOptionsGetter) + checkStorageErr(err) userStorage, err := useretcd.NewREST(c.RESTOptionsGetter) checkStorageErr(err) @@ -588,9 +591,10 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage { "projects": projectStorage, "projectRequests": projectRequestStorage, - "hostSubnets": hostSubnetStorage, - "netNamespaces": netNamespaceStorage, - "clusterNetworks": clusterNetworkStorage, + "hostSubnets": hostSubnetStorage, + "netNamespaces": netNamespaceStorage, + "clusterNetworks": clusterNetworkStorage, + "egressNetworkPolicies": egressNetworkPolicyStorage, "users": userStorage, "groups": groupStorage, diff --git a/pkg/sdn/api/deep_copy_generated.go b/pkg/sdn/api/deep_copy_generated.go index 1535c8dcf6ab..d456f9523f80 100644 --- a/pkg/sdn/api/deep_copy_generated.go +++ b/pkg/sdn/api/deep_copy_generated.go @@ -14,6 +14,11 @@ func init() { if err := api.Scheme.AddGeneratedDeepCopyFuncs( DeepCopy_api_ClusterNetwork, DeepCopy_api_ClusterNetworkList, + DeepCopy_api_EgressNetworkPolicy, + DeepCopy_api_EgressNetworkPolicyList, + DeepCopy_api_EgressNetworkPolicyPeer, + DeepCopy_api_EgressNetworkPolicyRule, + DeepCopy_api_EgressNetworkPolicySpec, DeepCopy_api_HostSubnet, DeepCopy_api_HostSubnetList, DeepCopy_api_NetNamespace, @@ -59,6 +64,68 @@ func DeepCopy_api_ClusterNetworkList(in ClusterNetworkList, out *ClusterNetworkL return nil } +func DeepCopy_api_EgressNetworkPolicy(in EgressNetworkPolicy, out *EgressNetworkPolicy, c *conversion.Cloner) error { + if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := api.DeepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := DeepCopy_api_EgressNetworkPolicySpec(in.Spec, &out.Spec, c); err != nil { + return err + } + return nil +} + +func DeepCopy_api_EgressNetworkPolicyList(in EgressNetworkPolicyList, out *EgressNetworkPolicyList, c *conversion.Cloner) error { + if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { + return err + } + if in.Items != nil { + in, out := in.Items, &out.Items + *out = make([]EgressNetworkPolicy, len(in)) + for i := range in { + if err := DeepCopy_api_EgressNetworkPolicy(in[i], &(*out)[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func DeepCopy_api_EgressNetworkPolicyPeer(in EgressNetworkPolicyPeer, out *EgressNetworkPolicyPeer, c *conversion.Cloner) error { + out.CIDRSelector = in.CIDRSelector + return nil +} + +func DeepCopy_api_EgressNetworkPolicyRule(in EgressNetworkPolicyRule, out *EgressNetworkPolicyRule, c *conversion.Cloner) error { + out.Type = in.Type + if err := DeepCopy_api_EgressNetworkPolicyPeer(in.To, &out.To, c); err != nil { + return err + } + return nil +} + +func DeepCopy_api_EgressNetworkPolicySpec(in EgressNetworkPolicySpec, out *EgressNetworkPolicySpec, c *conversion.Cloner) error { + if in.Egress != nil { + in, out := in.Egress, &out.Egress + *out = make([]EgressNetworkPolicyRule, len(in)) + for i := range in { + if err := DeepCopy_api_EgressNetworkPolicyRule(in[i], &(*out)[i], c); err != nil { + return err + } + } + } else { + out.Egress = nil + } + return nil +} + func DeepCopy_api_HostSubnet(in HostSubnet, out *HostSubnet, c *conversion.Cloner) error { if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err diff --git a/pkg/sdn/api/fields.go b/pkg/sdn/api/fields.go index 59318dd9ea6a..587f47c42379 100644 --- a/pkg/sdn/api/fields.go +++ b/pkg/sdn/api/fields.go @@ -22,3 +22,11 @@ func NetNamespaceToSelectableFields(obj *NetNamespace) fields.Set { "metadata.name": obj.Name, } } + +// EgressNetworkPolicyToSelectableFields returns a label set that represents the object +func EgressNetworkPolicyToSelectableFields(obj *EgressNetworkPolicy) fields.Set { + return fields.Set{ + "metadata.name": obj.Name, + "metadata.namespace": obj.Namespace, + } +} diff --git a/pkg/sdn/api/register.go b/pkg/sdn/api/register.go index 6831d7ef85cf..d988cc6e047a 100644 --- a/pkg/sdn/api/register.go +++ b/pkg/sdn/api/register.go @@ -34,12 +34,16 @@ func addKnownTypes(scheme *runtime.Scheme) { &HostSubnetList{}, &NetNamespace{}, &NetNamespaceList{}, + &EgressNetworkPolicy{}, + &EgressNetworkPolicyList{}, ) } -func (obj *ClusterNetwork) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ClusterNetworkList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *HostSubnet) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *HostSubnetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *NetNamespace) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *NetNamespaceList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *ClusterNetwork) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *ClusterNetworkList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *HostSubnet) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *HostSubnetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *NetNamespace) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *NetNamespaceList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *EgressNetworkPolicy) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *EgressNetworkPolicyList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/pkg/sdn/api/types.go b/pkg/sdn/api/types.go index 3108062b49f1..e0ae15d3e1ec 100644 --- a/pkg/sdn/api/types.go +++ b/pkg/sdn/api/types.go @@ -6,7 +6,8 @@ import ( ) const ( - ClusterNetworkDefault = "default" + ClusterNetworkDefault = "default" + EgressNetworkPolicyMaxRules = 50 ) // +genclient=true @@ -60,3 +61,42 @@ type NetNamespaceList struct { unversioned.ListMeta Items []NetNamespace } + +// EgressNetworkPolicyRuleType gives the type of an EgressNetworkPolicyRule +type EgressNetworkPolicyRuleType string + +const ( + EgressNetworkPolicyRuleAllow EgressNetworkPolicyRuleType = "Allow" + EgressNetworkPolicyRuleDeny EgressNetworkPolicyRuleType = "Deny" +) + +// EgressNetworkPolicyPeer specifies a target to apply egress policy to +type EgressNetworkPolicyPeer struct { + CIDRSelector string +} + +// EgressNetworkPolicyRule contains a single egress network policy rule +type EgressNetworkPolicyRule struct { + Type EgressNetworkPolicyRuleType + To EgressNetworkPolicyPeer +} + +// EgressNetworkPolicySpec provides a list of policies on outgoing traffic +type EgressNetworkPolicySpec struct { + Egress []EgressNetworkPolicyRule +} + +// EgressNetworkPolicy describes the current egress network policy +type EgressNetworkPolicy struct { + unversioned.TypeMeta + kapi.ObjectMeta + + Spec EgressNetworkPolicySpec +} + +// EgressNetworkPolicyList is a collection of EgressNetworkPolicy +type EgressNetworkPolicyList struct { + unversioned.TypeMeta + unversioned.ListMeta + Items []EgressNetworkPolicy +} diff --git a/pkg/sdn/api/v1/conversion.go b/pkg/sdn/api/v1/conversion.go index 38244a4bfbef..3c4c70473933 100644 --- a/pkg/sdn/api/v1/conversion.go +++ b/pkg/sdn/api/v1/conversion.go @@ -25,4 +25,10 @@ func addConversionFuncs(scheme *runtime.Scheme) { ); err != nil { panic(err) } + + if err := scheme.AddFieldLabelConversionFunc("v1", "EgressNetworkPolicy", + oapi.GetFieldLabelConversionFunc(api.EgressNetworkPolicyToSelectableFields(&api.EgressNetworkPolicy{}), nil), + ); err != nil { + panic(err) + } } diff --git a/pkg/sdn/api/v1/conversion_generated.go b/pkg/sdn/api/v1/conversion_generated.go index 94c543e88981..9479d6d728bc 100644 --- a/pkg/sdn/api/v1/conversion_generated.go +++ b/pkg/sdn/api/v1/conversion_generated.go @@ -17,6 +17,16 @@ func init() { Convert_api_ClusterNetwork_To_v1_ClusterNetwork, Convert_v1_ClusterNetworkList_To_api_ClusterNetworkList, Convert_api_ClusterNetworkList_To_v1_ClusterNetworkList, + Convert_v1_EgressNetworkPolicy_To_api_EgressNetworkPolicy, + Convert_api_EgressNetworkPolicy_To_v1_EgressNetworkPolicy, + Convert_v1_EgressNetworkPolicyList_To_api_EgressNetworkPolicyList, + Convert_api_EgressNetworkPolicyList_To_v1_EgressNetworkPolicyList, + Convert_v1_EgressNetworkPolicyPeer_To_api_EgressNetworkPolicyPeer, + Convert_api_EgressNetworkPolicyPeer_To_v1_EgressNetworkPolicyPeer, + Convert_v1_EgressNetworkPolicyRule_To_api_EgressNetworkPolicyRule, + Convert_api_EgressNetworkPolicyRule_To_v1_EgressNetworkPolicyRule, + Convert_v1_EgressNetworkPolicySpec_To_api_EgressNetworkPolicySpec, + Convert_api_EgressNetworkPolicySpec_To_v1_EgressNetworkPolicySpec, Convert_v1_HostSubnet_To_api_HostSubnet, Convert_api_HostSubnet_To_v1_HostSubnet, Convert_v1_HostSubnetList_To_api_HostSubnetList, @@ -117,6 +127,170 @@ func Convert_api_ClusterNetworkList_To_v1_ClusterNetworkList(in *sdn_api.Cluster return autoConvert_api_ClusterNetworkList_To_v1_ClusterNetworkList(in, out, s) } +func autoConvert_v1_EgressNetworkPolicy_To_api_EgressNetworkPolicy(in *EgressNetworkPolicy, out *sdn_api.EgressNetworkPolicy, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api_v1.Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_v1_EgressNetworkPolicySpec_To_api_EgressNetworkPolicySpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +func Convert_v1_EgressNetworkPolicy_To_api_EgressNetworkPolicy(in *EgressNetworkPolicy, out *sdn_api.EgressNetworkPolicy, s conversion.Scope) error { + return autoConvert_v1_EgressNetworkPolicy_To_api_EgressNetworkPolicy(in, out, s) +} + +func autoConvert_api_EgressNetworkPolicy_To_v1_EgressNetworkPolicy(in *sdn_api.EgressNetworkPolicy, out *EgressNetworkPolicy, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api_v1.Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := Convert_api_EgressNetworkPolicySpec_To_v1_EgressNetworkPolicySpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +func Convert_api_EgressNetworkPolicy_To_v1_EgressNetworkPolicy(in *sdn_api.EgressNetworkPolicy, out *EgressNetworkPolicy, s conversion.Scope) error { + return autoConvert_api_EgressNetworkPolicy_To_v1_EgressNetworkPolicy(in, out, s) +} + +func autoConvert_v1_EgressNetworkPolicyList_To_api_EgressNetworkPolicyList(in *EgressNetworkPolicyList, out *sdn_api.EgressNetworkPolicyList, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { + return err + } + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]sdn_api.EgressNetworkPolicy, len(*in)) + for i := range *in { + if err := Convert_v1_EgressNetworkPolicy_To_api_EgressNetworkPolicy(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_v1_EgressNetworkPolicyList_To_api_EgressNetworkPolicyList(in *EgressNetworkPolicyList, out *sdn_api.EgressNetworkPolicyList, s conversion.Scope) error { + return autoConvert_v1_EgressNetworkPolicyList_To_api_EgressNetworkPolicyList(in, out, s) +} + +func autoConvert_api_EgressNetworkPolicyList_To_v1_EgressNetworkPolicyList(in *sdn_api.EgressNetworkPolicyList, out *EgressNetworkPolicyList, s conversion.Scope) error { + if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { + return err + } + if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { + return err + } + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EgressNetworkPolicy, len(*in)) + for i := range *in { + if err := Convert_api_EgressNetworkPolicy_To_v1_EgressNetworkPolicy(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func Convert_api_EgressNetworkPolicyList_To_v1_EgressNetworkPolicyList(in *sdn_api.EgressNetworkPolicyList, out *EgressNetworkPolicyList, s conversion.Scope) error { + return autoConvert_api_EgressNetworkPolicyList_To_v1_EgressNetworkPolicyList(in, out, s) +} + +func autoConvert_v1_EgressNetworkPolicyPeer_To_api_EgressNetworkPolicyPeer(in *EgressNetworkPolicyPeer, out *sdn_api.EgressNetworkPolicyPeer, s conversion.Scope) error { + out.CIDRSelector = in.CIDRSelector + return nil +} + +func Convert_v1_EgressNetworkPolicyPeer_To_api_EgressNetworkPolicyPeer(in *EgressNetworkPolicyPeer, out *sdn_api.EgressNetworkPolicyPeer, s conversion.Scope) error { + return autoConvert_v1_EgressNetworkPolicyPeer_To_api_EgressNetworkPolicyPeer(in, out, s) +} + +func autoConvert_api_EgressNetworkPolicyPeer_To_v1_EgressNetworkPolicyPeer(in *sdn_api.EgressNetworkPolicyPeer, out *EgressNetworkPolicyPeer, s conversion.Scope) error { + out.CIDRSelector = in.CIDRSelector + return nil +} + +func Convert_api_EgressNetworkPolicyPeer_To_v1_EgressNetworkPolicyPeer(in *sdn_api.EgressNetworkPolicyPeer, out *EgressNetworkPolicyPeer, s conversion.Scope) error { + return autoConvert_api_EgressNetworkPolicyPeer_To_v1_EgressNetworkPolicyPeer(in, out, s) +} + +func autoConvert_v1_EgressNetworkPolicyRule_To_api_EgressNetworkPolicyRule(in *EgressNetworkPolicyRule, out *sdn_api.EgressNetworkPolicyRule, s conversion.Scope) error { + out.Type = sdn_api.EgressNetworkPolicyRuleType(in.Type) + if err := Convert_v1_EgressNetworkPolicyPeer_To_api_EgressNetworkPolicyPeer(&in.To, &out.To, s); err != nil { + return err + } + return nil +} + +func Convert_v1_EgressNetworkPolicyRule_To_api_EgressNetworkPolicyRule(in *EgressNetworkPolicyRule, out *sdn_api.EgressNetworkPolicyRule, s conversion.Scope) error { + return autoConvert_v1_EgressNetworkPolicyRule_To_api_EgressNetworkPolicyRule(in, out, s) +} + +func autoConvert_api_EgressNetworkPolicyRule_To_v1_EgressNetworkPolicyRule(in *sdn_api.EgressNetworkPolicyRule, out *EgressNetworkPolicyRule, s conversion.Scope) error { + out.Type = EgressNetworkPolicyRuleType(in.Type) + if err := Convert_api_EgressNetworkPolicyPeer_To_v1_EgressNetworkPolicyPeer(&in.To, &out.To, s); err != nil { + return err + } + return nil +} + +func Convert_api_EgressNetworkPolicyRule_To_v1_EgressNetworkPolicyRule(in *sdn_api.EgressNetworkPolicyRule, out *EgressNetworkPolicyRule, s conversion.Scope) error { + return autoConvert_api_EgressNetworkPolicyRule_To_v1_EgressNetworkPolicyRule(in, out, s) +} + +func autoConvert_v1_EgressNetworkPolicySpec_To_api_EgressNetworkPolicySpec(in *EgressNetworkPolicySpec, out *sdn_api.EgressNetworkPolicySpec, s conversion.Scope) error { + if in.Egress != nil { + in, out := &in.Egress, &out.Egress + *out = make([]sdn_api.EgressNetworkPolicyRule, len(*in)) + for i := range *in { + if err := Convert_v1_EgressNetworkPolicyRule_To_api_EgressNetworkPolicyRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Egress = nil + } + return nil +} + +func Convert_v1_EgressNetworkPolicySpec_To_api_EgressNetworkPolicySpec(in *EgressNetworkPolicySpec, out *sdn_api.EgressNetworkPolicySpec, s conversion.Scope) error { + return autoConvert_v1_EgressNetworkPolicySpec_To_api_EgressNetworkPolicySpec(in, out, s) +} + +func autoConvert_api_EgressNetworkPolicySpec_To_v1_EgressNetworkPolicySpec(in *sdn_api.EgressNetworkPolicySpec, out *EgressNetworkPolicySpec, s conversion.Scope) error { + if in.Egress != nil { + in, out := &in.Egress, &out.Egress + *out = make([]EgressNetworkPolicyRule, len(*in)) + for i := range *in { + if err := Convert_api_EgressNetworkPolicyRule_To_v1_EgressNetworkPolicyRule(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Egress = nil + } + return nil +} + +func Convert_api_EgressNetworkPolicySpec_To_v1_EgressNetworkPolicySpec(in *sdn_api.EgressNetworkPolicySpec, out *EgressNetworkPolicySpec, s conversion.Scope) error { + return autoConvert_api_EgressNetworkPolicySpec_To_v1_EgressNetworkPolicySpec(in, out, s) +} + func autoConvert_v1_HostSubnet_To_api_HostSubnet(in *HostSubnet, out *sdn_api.HostSubnet, s conversion.Scope) error { if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { return err diff --git a/pkg/sdn/api/v1/conversion_test.go b/pkg/sdn/api/v1/conversion_test.go index d0e11b72802c..1070eb1a3f68 100644 --- a/pkg/sdn/api/v1/conversion_test.go +++ b/pkg/sdn/api/v1/conversion_test.go @@ -26,4 +26,8 @@ func TestFieldSelectorConversions(t *testing.T) { api.NetNamespaceToSelectableFields(&api.NetNamespace{}), ) + testutil.CheckFieldLabelConversions(t, "v1", "EgressNetworkPolicy", + // Ensure all currently returned labels are supported + api.EgressNetworkPolicyToSelectableFields(&api.EgressNetworkPolicy{}), + ) } diff --git a/pkg/sdn/api/v1/deep_copy_generated.go b/pkg/sdn/api/v1/deep_copy_generated.go index c1f5071d4169..073b21d6c45d 100644 --- a/pkg/sdn/api/v1/deep_copy_generated.go +++ b/pkg/sdn/api/v1/deep_copy_generated.go @@ -15,6 +15,11 @@ func init() { if err := api.Scheme.AddGeneratedDeepCopyFuncs( DeepCopy_v1_ClusterNetwork, DeepCopy_v1_ClusterNetworkList, + DeepCopy_v1_EgressNetworkPolicy, + DeepCopy_v1_EgressNetworkPolicyList, + DeepCopy_v1_EgressNetworkPolicyPeer, + DeepCopy_v1_EgressNetworkPolicyRule, + DeepCopy_v1_EgressNetworkPolicySpec, DeepCopy_v1_HostSubnet, DeepCopy_v1_HostSubnetList, DeepCopy_v1_NetNamespace, @@ -60,6 +65,68 @@ func DeepCopy_v1_ClusterNetworkList(in ClusterNetworkList, out *ClusterNetworkLi return nil } +func DeepCopy_v1_EgressNetworkPolicy(in EgressNetworkPolicy, out *EgressNetworkPolicy, c *conversion.Cloner) error { + if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := api_v1.DeepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := DeepCopy_v1_EgressNetworkPolicySpec(in.Spec, &out.Spec, c); err != nil { + return err + } + return nil +} + +func DeepCopy_v1_EgressNetworkPolicyList(in EgressNetworkPolicyList, out *EgressNetworkPolicyList, c *conversion.Cloner) error { + if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := unversioned.DeepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { + return err + } + if in.Items != nil { + in, out := in.Items, &out.Items + *out = make([]EgressNetworkPolicy, len(in)) + for i := range in { + if err := DeepCopy_v1_EgressNetworkPolicy(in[i], &(*out)[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func DeepCopy_v1_EgressNetworkPolicyPeer(in EgressNetworkPolicyPeer, out *EgressNetworkPolicyPeer, c *conversion.Cloner) error { + out.CIDRSelector = in.CIDRSelector + return nil +} + +func DeepCopy_v1_EgressNetworkPolicyRule(in EgressNetworkPolicyRule, out *EgressNetworkPolicyRule, c *conversion.Cloner) error { + out.Type = in.Type + if err := DeepCopy_v1_EgressNetworkPolicyPeer(in.To, &out.To, c); err != nil { + return err + } + return nil +} + +func DeepCopy_v1_EgressNetworkPolicySpec(in EgressNetworkPolicySpec, out *EgressNetworkPolicySpec, c *conversion.Cloner) error { + if in.Egress != nil { + in, out := in.Egress, &out.Egress + *out = make([]EgressNetworkPolicyRule, len(in)) + for i := range in { + if err := DeepCopy_v1_EgressNetworkPolicyRule(in[i], &(*out)[i], c); err != nil { + return err + } + } + } else { + out.Egress = nil + } + return nil +} + func DeepCopy_v1_HostSubnet(in HostSubnet, out *HostSubnet, c *conversion.Cloner) error { if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err diff --git a/pkg/sdn/api/v1/generated.pb.go b/pkg/sdn/api/v1/generated.pb.go index 39432b5213f0..3df1d8f35c67 100644 --- a/pkg/sdn/api/v1/generated.pb.go +++ b/pkg/sdn/api/v1/generated.pb.go @@ -11,6 +11,11 @@ It has these top-level messages: ClusterNetwork ClusterNetworkList + EgressNetworkPolicy + EgressNetworkPolicyList + EgressNetworkPolicyPeer + EgressNetworkPolicyRule + EgressNetworkPolicySpec HostSubnet HostSubnetList NetNamespace @@ -37,6 +42,26 @@ func (m *ClusterNetworkList) Reset() { *m = ClusterNetworkList{} } func (m *ClusterNetworkList) String() string { return proto.CompactTextString(m) } func (*ClusterNetworkList) ProtoMessage() {} +func (m *EgressNetworkPolicy) Reset() { *m = EgressNetworkPolicy{} } +func (m *EgressNetworkPolicy) String() string { return proto.CompactTextString(m) } +func (*EgressNetworkPolicy) ProtoMessage() {} + +func (m *EgressNetworkPolicyList) Reset() { *m = EgressNetworkPolicyList{} } +func (m *EgressNetworkPolicyList) String() string { return proto.CompactTextString(m) } +func (*EgressNetworkPolicyList) ProtoMessage() {} + +func (m *EgressNetworkPolicyPeer) Reset() { *m = EgressNetworkPolicyPeer{} } +func (m *EgressNetworkPolicyPeer) String() string { return proto.CompactTextString(m) } +func (*EgressNetworkPolicyPeer) ProtoMessage() {} + +func (m *EgressNetworkPolicyRule) Reset() { *m = EgressNetworkPolicyRule{} } +func (m *EgressNetworkPolicyRule) String() string { return proto.CompactTextString(m) } +func (*EgressNetworkPolicyRule) ProtoMessage() {} + +func (m *EgressNetworkPolicySpec) Reset() { *m = EgressNetworkPolicySpec{} } +func (m *EgressNetworkPolicySpec) String() string { return proto.CompactTextString(m) } +func (*EgressNetworkPolicySpec) ProtoMessage() {} + func (m *HostSubnet) Reset() { *m = HostSubnet{} } func (m *HostSubnet) String() string { return proto.CompactTextString(m) } func (*HostSubnet) ProtoMessage() {} @@ -56,6 +81,11 @@ func (*NetNamespaceList) ProtoMessage() {} func init() { proto.RegisterType((*ClusterNetwork)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.ClusterNetwork") proto.RegisterType((*ClusterNetworkList)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.ClusterNetworkList") + proto.RegisterType((*EgressNetworkPolicy)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.EgressNetworkPolicy") + proto.RegisterType((*EgressNetworkPolicyList)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.EgressNetworkPolicyList") + proto.RegisterType((*EgressNetworkPolicyPeer)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.EgressNetworkPolicyPeer") + proto.RegisterType((*EgressNetworkPolicyRule)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.EgressNetworkPolicyRule") + proto.RegisterType((*EgressNetworkPolicySpec)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.EgressNetworkPolicySpec") proto.RegisterType((*HostSubnet)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.HostSubnet") proto.RegisterType((*HostSubnetList)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.HostSubnetList") proto.RegisterType((*NetNamespace)(nil), "gitpro.ttaallkk.top.openshift.origin.pkg.sdn.api.v1.NetNamespace") @@ -140,7 +170,7 @@ func (m *ClusterNetworkList) MarshalTo(data []byte) (int, error) { return i, nil } -func (m *HostSubnet) Marshal() (data []byte, err error) { +func (m *EgressNetworkPolicy) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) n, err := m.MarshalTo(data) @@ -150,7 +180,7 @@ func (m *HostSubnet) Marshal() (data []byte, err error) { return data[:n], nil } -func (m *HostSubnet) MarshalTo(data []byte) (int, error) { +func (m *EgressNetworkPolicy) MarshalTo(data []byte) (int, error) { var i int _ = i var l int @@ -165,6 +195,160 @@ func (m *HostSubnet) MarshalTo(data []byte) (int, error) { i += n3 data[i] = 0x12 i++ + i = encodeVarintGenerated(data, i, uint64(m.Spec.Size())) + n4, err := m.Spec.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n4 + return i, nil +} + +func (m *EgressNetworkPolicyList) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *EgressNetworkPolicyList) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + data[i] = 0xa + i++ + i = encodeVarintGenerated(data, i, uint64(m.ListMeta.Size())) + n5, err := m.ListMeta.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n5 + if len(m.Items) > 0 { + for _, msg := range m.Items { + data[i] = 0x12 + i++ + i = encodeVarintGenerated(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *EgressNetworkPolicyPeer) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *EgressNetworkPolicyPeer) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + data[i] = 0xa + i++ + i = encodeVarintGenerated(data, i, uint64(len(m.CIDRSelector))) + i += copy(data[i:], m.CIDRSelector) + return i, nil +} + +func (m *EgressNetworkPolicyRule) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *EgressNetworkPolicyRule) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + data[i] = 0xa + i++ + i = encodeVarintGenerated(data, i, uint64(len(m.Type))) + i += copy(data[i:], m.Type) + data[i] = 0x12 + i++ + i = encodeVarintGenerated(data, i, uint64(m.To.Size())) + n6, err := m.To.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n6 + return i, nil +} + +func (m *EgressNetworkPolicySpec) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *EgressNetworkPolicySpec) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Egress) > 0 { + for _, msg := range m.Egress { + data[i] = 0xa + i++ + i = encodeVarintGenerated(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *HostSubnet) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *HostSubnet) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + data[i] = 0xa + i++ + i = encodeVarintGenerated(data, i, uint64(m.ObjectMeta.Size())) + n7, err := m.ObjectMeta.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n7 + data[i] = 0x12 + i++ i = encodeVarintGenerated(data, i, uint64(len(m.Host))) i += copy(data[i:], m.Host) data[i] = 0x1a @@ -196,11 +380,11 @@ func (m *HostSubnetList) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintGenerated(data, i, uint64(m.ListMeta.Size())) - n4, err := m.ListMeta.MarshalTo(data[i:]) + n8, err := m.ListMeta.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n4 + i += n8 if len(m.Items) > 0 { for _, msg := range m.Items { data[i] = 0x12 @@ -234,11 +418,11 @@ func (m *NetNamespace) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintGenerated(data, i, uint64(m.ObjectMeta.Size())) - n5, err := m.ObjectMeta.MarshalTo(data[i:]) + n9, err := m.ObjectMeta.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n5 + i += n9 data[i] = 0x12 i++ i = encodeVarintGenerated(data, i, uint64(len(m.NetName))) @@ -267,11 +451,11 @@ func (m *NetNamespaceList) MarshalTo(data []byte) (int, error) { data[i] = 0xa i++ i = encodeVarintGenerated(data, i, uint64(m.ListMeta.Size())) - n6, err := m.ListMeta.MarshalTo(data[i:]) + n10, err := m.ListMeta.MarshalTo(data[i:]) if err != nil { return 0, err } - i += n6 + i += n10 if len(m.Items) > 0 { for _, msg := range m.Items { data[i] = 0x12 @@ -343,6 +527,60 @@ func (m *ClusterNetworkList) Size() (n int) { return n } +func (m *EgressNetworkPolicy) Size() (n int) { + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + l = m.Spec.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *EgressNetworkPolicyList) Size() (n int) { + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *EgressNetworkPolicyPeer) Size() (n int) { + var l int + _ = l + l = len(m.CIDRSelector) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *EgressNetworkPolicyRule) Size() (n int) { + var l int + _ = l + l = len(m.Type) + n += 1 + l + sovGenerated(uint64(l)) + l = m.To.Size() + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *EgressNetworkPolicySpec) Size() (n int) { + var l int + _ = l + if len(m.Egress) > 0 { + for _, e := range m.Egress { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + func (m *HostSubnet) Size() (n int) { var l int _ = l @@ -706,6 +944,496 @@ func (m *ClusterNetworkList) Unmarshal(data []byte) error { } return nil } +func (m *EgressNetworkPolicy) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressNetworkPolicy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressNetworkPolicy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Spec.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EgressNetworkPolicyList) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressNetworkPolicyList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressNetworkPolicyList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, EgressNetworkPolicy{}) + if err := m.Items[len(m.Items)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EgressNetworkPolicyPeer) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressNetworkPolicyPeer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressNetworkPolicyPeer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CIDRSelector", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CIDRSelector = string(data[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EgressNetworkPolicyRule) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressNetworkPolicyRule: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressNetworkPolicyRule: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = EgressNetworkPolicyRuleType(data[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field To", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.To.Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EgressNetworkPolicySpec) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressNetworkPolicySpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressNetworkPolicySpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Egress", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Egress = append(m.Egress, EgressNetworkPolicyRule{}) + if err := m.Egress[len(m.Egress)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *HostSubnet) Unmarshal(data []byte) error { l := len(data) iNdEx := 0 diff --git a/pkg/sdn/api/v1/generated.proto b/pkg/sdn/api/v1/generated.proto index c5f4ec3e58a9..c336eb244473 100644 --- a/pkg/sdn/api/v1/generated.proto +++ b/pkg/sdn/api/v1/generated.proto @@ -39,6 +39,45 @@ message ClusterNetworkList { repeated ClusterNetwork items = 2; } +// EgressNetworkPolicy describes the current egress network policy +message EgressNetworkPolicy { + // Standard object's metadata. + optional k8s.io.kubernetes.pkg.api.v1.ObjectMeta metadata = 1; + + // Spec is the specification of the current egress network policy + optional EgressNetworkPolicySpec spec = 2; +} + +// EgressNetworkPolicyList is a collection of EgressNetworkPolicy +message EgressNetworkPolicyList { + // Standard object's metadata. + optional k8s.io.kubernetes.pkg.api.unversioned.ListMeta metadata = 1; + + // Items is the list of firewalls + repeated EgressNetworkPolicy items = 2; +} + +// EgressNetworkPolicyPeer specifies a target to apply egress policy to +message EgressNetworkPolicyPeer { + // CIDRSelector is the CIDR range to allow/deny traffic to + optional string cidrSelector = 1; +} + +// EgressNetworkPolicyRule contains a single egress network policy rule +message EgressNetworkPolicyRule { + // Type marks this as an "allow" or "deny" rule + optional string type = 1; + + // To is the target that traffic is allowed/denied to + optional EgressNetworkPolicyPeer to = 2; +} + +// EgressNetworkPolicySpec provides a list of policies on outgoing traffic +message EgressNetworkPolicySpec { + // Egress contains the list of egress policy rules + repeated EgressNetworkPolicyRule egress = 1; +} + // HostSubnet encapsulates the inputs needed to define the container subnet network on a node message HostSubnet { // Standard object's metadata. diff --git a/pkg/sdn/api/v1/register.go b/pkg/sdn/api/v1/register.go index d816995e6e0f..fae07b17206f 100644 --- a/pkg/sdn/api/v1/register.go +++ b/pkg/sdn/api/v1/register.go @@ -24,12 +24,16 @@ func addKnownTypes(scheme *runtime.Scheme) { &HostSubnetList{}, &NetNamespace{}, &NetNamespaceList{}, + &EgressNetworkPolicy{}, + &EgressNetworkPolicyList{}, ) } -func (obj *ClusterNetwork) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ClusterNetworkList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *HostSubnet) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *HostSubnetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *NetNamespace) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *NetNamespaceList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *ClusterNetwork) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *ClusterNetworkList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *HostSubnet) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *HostSubnetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *NetNamespace) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *NetNamespaceList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *EgressNetworkPolicy) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *EgressNetworkPolicyList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } diff --git a/pkg/sdn/api/v1/swagger_doc.go b/pkg/sdn/api/v1/swagger_doc.go index d6484d9d071d..96ca2d37a44a 100644 --- a/pkg/sdn/api/v1/swagger_doc.go +++ b/pkg/sdn/api/v1/swagger_doc.go @@ -28,6 +28,54 @@ func (ClusterNetworkList) SwaggerDoc() map[string]string { return map_ClusterNetworkList } +var map_EgressNetworkPolicy = map[string]string{ + "": "EgressNetworkPolicy describes the current egress network policy for a Namespace. When using the 'redhat/openshift-ovs-multitenant' network plugin, traffic from a pod to an IP address outside the cluster will be checked against each EgressNetworkPolicyRule in the pod's namespace's EgressNetworkPolicy, in order. If no rule matches (or no EgressNetworkPolicy is present) then the traffic will be allowed by default.", + "metadata": "metadata for EgressNetworkPolicy", + "spec": "spec is the specification of the current egress network policy", +} + +func (EgressNetworkPolicy) SwaggerDoc() map[string]string { + return map_EgressNetworkPolicy +} + +var map_EgressNetworkPolicyList = map[string]string{ + "": "EgressNetworkPolicyList is a collection of EgressNetworkPolicy", + "metadata": "metadata for EgressNetworkPolicyList", + "items": "items is the list of policies", +} + +func (EgressNetworkPolicyList) SwaggerDoc() map[string]string { + return map_EgressNetworkPolicyList +} + +var map_EgressNetworkPolicyPeer = map[string]string{ + "": "EgressNetworkPolicyPeer specifies a target to apply egress network policy to", + "cidrSelector": "cidrSelector is the CIDR range to allow/deny traffic to", +} + +func (EgressNetworkPolicyPeer) SwaggerDoc() map[string]string { + return map_EgressNetworkPolicyPeer +} + +var map_EgressNetworkPolicyRule = map[string]string{ + "": "EgressNetworkPolicyRule contains a single egress network policy rule", + "type": "type marks this as an \"Allow\" or \"Deny\" rule", + "to": "to is the target that traffic is allowed/denied to", +} + +func (EgressNetworkPolicyRule) SwaggerDoc() map[string]string { + return map_EgressNetworkPolicyRule +} + +var map_EgressNetworkPolicySpec = map[string]string{ + "": "EgressNetworkPolicySpec provides a list of policies on outgoing network traffic", + "egress": "egress contains the list of egress policy rules", +} + +func (EgressNetworkPolicySpec) SwaggerDoc() map[string]string { + return map_EgressNetworkPolicySpec +} + var map_HostSubnet = map[string]string{ "": "HostSubnet encapsulates the inputs needed to define the container subnet network on a node", "metadata": "Standard object's metadata.", diff --git a/pkg/sdn/api/v1/types.go b/pkg/sdn/api/v1/types.go index b1b7487447e8..bce93bccac3e 100644 --- a/pkg/sdn/api/v1/types.go +++ b/pkg/sdn/api/v1/types.go @@ -80,3 +80,54 @@ type NetNamespaceList struct { // Items is the list of net namespaces Items []NetNamespace `json:"items" protobuf:"bytes,2,rep,name=items"` } + +// EgressNetworkPolicyRuleType indicates whether an EgressNetworkPolicyRule allows or denies traffic +type EgressNetworkPolicyRuleType string + +const ( + EgressNetworkPolicyRuleAllow EgressNetworkPolicyRuleType = "Allow" + EgressNetworkPolicyRuleDeny EgressNetworkPolicyRuleType = "Deny" +) + +// EgressNetworkPolicyPeer specifies a target to apply egress network policy to +type EgressNetworkPolicyPeer struct { + // cidrSelector is the CIDR range to allow/deny traffic to + CIDRSelector string `json:"cidrSelector" protobuf:"bytes,1,rep,name=cidrSelector"` +} + +// EgressNetworkPolicyRule contains a single egress network policy rule +type EgressNetworkPolicyRule struct { + // type marks this as an "Allow" or "Deny" rule + Type EgressNetworkPolicyRuleType `json:"type" protobuf:"bytes,1,rep,name=type"` + // to is the target that traffic is allowed/denied to + To EgressNetworkPolicyPeer `json:"to" protobuf:"bytes,2,rep,name=to"` +} + +// EgressNetworkPolicySpec provides a list of policies on outgoing network traffic +type EgressNetworkPolicySpec struct { + // egress contains the list of egress policy rules + Egress []EgressNetworkPolicyRule `json:"egress" protobuf:"bytes,1,rep,name=egress"` +} + +// EgressNetworkPolicy describes the current egress network policy for a Namespace. When using +// the 'redhat/openshift-ovs-multitenant' network plugin, traffic from a pod to an IP address +// outside the cluster will be checked against each EgressNetworkPolicyRule in the pod's +// namespace's EgressNetworkPolicy, in order. If no rule matches (or no EgressNetworkPolicy +// is present) then the traffic will be allowed by default. +type EgressNetworkPolicy struct { + unversioned.TypeMeta `json:",inline"` + // metadata for EgressNetworkPolicy + kapi.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // spec is the specification of the current egress network policy + Spec EgressNetworkPolicySpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` +} + +// EgressNetworkPolicyList is a collection of EgressNetworkPolicy +type EgressNetworkPolicyList struct { + unversioned.TypeMeta `json:",inline"` + // metadata for EgressNetworkPolicyList + unversioned.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + // items is the list of policies + Items []EgressNetworkPolicy `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/pkg/sdn/api/validation/validation.go b/pkg/sdn/api/validation/validation.go index 03443e8a04c7..acf81017b8bc 100644 --- a/pkg/sdn/api/validation/validation.go +++ b/pkg/sdn/api/validation/validation.go @@ -117,3 +117,29 @@ func ValidateNetNamespace(netnamespace *sdnapi.NetNamespace) field.ErrorList { func ValidateNetNamespaceUpdate(obj *sdnapi.NetNamespace, old *sdnapi.NetNamespace) field.ErrorList { return validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata")) } + +// ValidateEgressNetworkPolicy tests if required fields in the EgressNetworkPolicy are set. +func ValidateEgressNetworkPolicy(policy *sdnapi.EgressNetworkPolicy) field.ErrorList { + allErrs := validation.ValidateObjectMeta(&policy.ObjectMeta, true, oapi.MinimalNameRequirements, field.NewPath("metadata")) + + for i, rule := range policy.Spec.Egress { + if rule.Type != sdnapi.EgressNetworkPolicyRuleAllow && rule.Type != sdnapi.EgressNetworkPolicyRuleDeny { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("egress").Index(i).Child("type"), rule.Type, "invalid policy type")) + } + + _, _, err := net.ParseCIDR(rule.To.CIDRSelector) + if err != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("egress").Index(i).Child("to"), rule.To.CIDRSelector, err.Error())) + } + } + + if len(policy.Spec.Egress) > sdnapi.EgressNetworkPolicyMaxRules { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("egress"), "", ("too many egress rules (max 50)"))) + } + + return allErrs +} + +func ValidateEgressNetworkPolicyUpdate(obj *sdnapi.EgressNetworkPolicy, old *sdnapi.EgressNetworkPolicy) field.ErrorList { + return validation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata")) +} diff --git a/pkg/sdn/api/validation/validation_test.go b/pkg/sdn/api/validation/validation_test.go index 88f3f2c2bc0e..56f3aeacfa9a 100644 --- a/pkg/sdn/api/validation/validation_test.go +++ b/pkg/sdn/api/validation/validation_test.go @@ -139,3 +139,111 @@ func TestValidateHostSubnet(t *testing.T) { } } } + +func TestValidateEgressNetworkPolicy(t *testing.T) { + tests := []struct { + name string + fw *api.EgressNetworkPolicy + expectedErrors int + }{ + { + name: "Empty", + fw: &api.EgressNetworkPolicy{ + ObjectMeta: kapi.ObjectMeta{ + Name: "default", + Namespace: "testing", + }, + Spec: api.EgressNetworkPolicySpec{ + Egress: []api.EgressNetworkPolicyRule{}, + }, + }, + expectedErrors: 0, + }, + { + name: "Good one", + fw: &api.EgressNetworkPolicy{ + ObjectMeta: kapi.ObjectMeta{ + Name: "default", + Namespace: "testing", + }, + Spec: api.EgressNetworkPolicySpec{ + Egress: []api.EgressNetworkPolicyRule{ + { + Type: api.EgressNetworkPolicyRuleAllow, + To: api.EgressNetworkPolicyPeer{ + CIDRSelector: "1.2.3.0/24", + }, + }, + { + Type: api.EgressNetworkPolicyRuleDeny, + To: api.EgressNetworkPolicyPeer{ + CIDRSelector: "1.2.3.4/32", + }, + }, + }, + }, + }, + expectedErrors: 0, + }, + { + name: "Bad policy", + fw: &api.EgressNetworkPolicy{ + ObjectMeta: kapi.ObjectMeta{ + Name: "default", + Namespace: "testing", + }, + Spec: api.EgressNetworkPolicySpec{ + Egress: []api.EgressNetworkPolicyRule{ + { + Type: api.EgressNetworkPolicyRuleType("Bob"), + To: api.EgressNetworkPolicyPeer{ + CIDRSelector: "1.2.3.0/24", + }, + }, + { + Type: api.EgressNetworkPolicyRuleDeny, + To: api.EgressNetworkPolicyPeer{ + CIDRSelector: "1.2.3.4/32", + }, + }, + }, + }, + }, + expectedErrors: 1, + }, + { + name: "Bad destination", + fw: &api.EgressNetworkPolicy{ + ObjectMeta: kapi.ObjectMeta{ + Name: "default", + Namespace: "testing", + }, + Spec: api.EgressNetworkPolicySpec{ + Egress: []api.EgressNetworkPolicyRule{ + { + Type: api.EgressNetworkPolicyRuleAllow, + To: api.EgressNetworkPolicyPeer{ + CIDRSelector: "1.2.3.4", + }, + }, + { + Type: api.EgressNetworkPolicyRuleDeny, + To: api.EgressNetworkPolicyPeer{ + CIDRSelector: "", + }, + }, + }, + }, + }, + expectedErrors: 2, + }, + } + + for _, tc := range tests { + errs := ValidateEgressNetworkPolicy(tc.fw) + + if len(errs) != tc.expectedErrors { + t.Errorf("Test case %s expected %d error(s), got %d. %v", tc.name, tc.expectedErrors, len(errs), errs) + } + } +} diff --git a/pkg/sdn/plugin/controller.go b/pkg/sdn/plugin/controller.go index 92298bf66dcb..31ec719a36a4 100644 --- a/pkg/sdn/plugin/controller.go +++ b/pkg/sdn/plugin/controller.go @@ -16,7 +16,10 @@ import ( kapi "k8s.io/kubernetes/pkg/api" kexec "k8s.io/kubernetes/pkg/util/exec" + utilruntime "k8s.io/kubernetes/pkg/util/runtime" "k8s.io/kubernetes/pkg/util/sysctl" + utilwait "k8s.io/kubernetes/pkg/util/wait" + "k8s.io/kubernetes/pkg/watch" ) const ( @@ -254,7 +257,7 @@ func (plugin *OsdnNode) SetupSDN(localSubnetCIDR, clusterNetworkCIDR, servicesNe otx.AddFlow("table=5, priority=200, ip, nw_dst=%s, actions=goto_table:7", localSubnetCIDR) otx.AddFlow("table=5, priority=100, arp, nw_dst=%s, actions=goto_table:8", clusterNetworkCIDR) otx.AddFlow("table=5, priority=100, ip, nw_dst=%s, actions=goto_table:8", clusterNetworkCIDR) - otx.AddFlow("table=5, priority=0, ip, actions=output:2") + otx.AddFlow("table=5, priority=0, ip, actions=goto_table:9") otx.AddFlow("table=5, priority=0, arp, actions=drop") // Table 6: ARP to container, filled in by openshift-sdn-ovs @@ -271,6 +274,10 @@ func (plugin *OsdnNode) SetupSDN(localSubnetCIDR, clusterNetworkCIDR, servicesNe // eg, "table=8, priority=100, ip, nw_dst=${remote_subnet_cidr}, actions=move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31], set_field:${remote_node_ip}->tun_dst,output:1" otx.AddFlow("table=8, priority=0, actions=drop") + // Table 9: egress network policy dispatch; edited by updateEgressNetworkPolicy() + // eg, "table=9, reg0=${tenant_id}, priority=2, ip, nw_dst=${external_cidr}, actions=drop + otx.AddFlow("table=9, priority=0, actions=output:2") + err = otx.EndTransaction() if err != nil { return false, err @@ -329,6 +336,155 @@ func (plugin *OsdnNode) SetupSDN(localSubnetCIDR, clusterNetworkCIDR, servicesNe return true, nil } +func (plugin *OsdnNode) SetupEgressNetworkPolicy() error { + policies, err := plugin.registry.GetEgressNetworkPolicies() + if err != nil { + return fmt.Errorf("Could not get EgressNetworkPolicies: %s", err) + } + + for _, policy := range policies { + vnid, err := plugin.vnids.GetVNID(policy.Namespace) + if err != nil { + glog.Warningf("Could not find netid for namespace %q: %v", policy.Namespace, err) + continue + } + plugin.egressPolicies[vnid] = append(plugin.egressPolicies[vnid], &policy) + } + + for vnid := range plugin.egressPolicies { + err := plugin.updateEgressNetworkPolicy(vnid) + if err != nil { + return err + } + } + + go utilwait.Forever(plugin.watchEgressNetworkPolicies, 0) + return nil +} + +func (plugin *OsdnNode) watchEgressNetworkPolicies() { + eventQueue := plugin.registry.RunEventQueue(EgressNetworkPolicies) + + for { + eventType, obj, err := eventQueue.Pop() + if err != nil { + utilruntime.HandleError(fmt.Errorf("EventQueue failed for EgressNetworkPolicy: %v", err)) + return + } + policy := obj.(*osapi.EgressNetworkPolicy) + + vnid, err := plugin.vnids.GetVNID(policy.Namespace) + if err != nil { + glog.Warningf("Could not find netid for namespace %q: %v", policy.Namespace, err) + continue + } + + policies := plugin.egressPolicies[vnid] + for i, oldPolicy := range policies { + if oldPolicy.UID == policy.UID { + policies = append(policies[:i], policies[i+1:]...) + break + } + } + if eventType != watch.Deleted && len(policy.Spec.Egress) > 0 { + policies = append(policies, policy) + } + plugin.egressPolicies[vnid] = policies + + err = plugin.updateEgressNetworkPolicy(vnid) + if err != nil { + utilruntime.HandleError(err) + return + } + } +} + +func (plugin *OsdnNode) UpdateEgressNetworkPolicyVNID(namespace string, oldVnid, newVnid uint32) error { + var policy *osapi.EgressNetworkPolicy + + policies := plugin.egressPolicies[oldVnid] + for i, oldPolicy := range policies { + if oldPolicy.Namespace == namespace { + policy = oldPolicy + plugin.egressPolicies[oldVnid] = append(policies[:i], policies[i+1:]...) + err := plugin.updateEgressNetworkPolicy(oldVnid) + if err != nil { + return err + } + break + } + } + + if policy != nil { + plugin.egressPolicies[newVnid] = append(plugin.egressPolicies[newVnid], policy) + err := plugin.updateEgressNetworkPolicy(newVnid) + if err != nil { + return err + } + } + + return nil +} + +func policyNames(policies []*osapi.EgressNetworkPolicy) string { + names := make([]string, len(policies)) + for i, policy := range policies { + names[i] = policy.Namespace + ":" + policy.Name + } + return strings.Join(names, ", ") +} + +func (plugin *OsdnNode) updateEgressNetworkPolicy(vnid uint32) error { + otx := ovs.NewTransaction(kexec.New(), BR) + + policies := plugin.egressPolicies[vnid] + namespaces := plugin.vnids.GetNamespaces(vnid) + if len(policies) == 0 { + otx.DeleteFlows("table=9, reg0=%d", vnid) + } else if vnid == 0 { + glog.Errorf("EgressNetworkPolicy in global network namespace is not allowed (%s); ignoring", policyNames(policies)) + } else if len(namespaces) > 1 { + glog.Errorf("EgressNetworkPolicy not allowed in shared NetNamespace (%s); dropping all traffic", strings.Join(namespaces, ", ")) + otx.DeleteFlows("table=9, reg0=%d", vnid) + otx.AddFlow("table=9, reg0=%d, priority=1, actions=drop", vnid) + } else if len(policies) > 1 { + glog.Errorf("multiple EgressNetworkPolicies in same network namespace (%s) is not allowed; dropping all traffic", policyNames(policies)) + otx.DeleteFlows("table=9, reg0=%d", vnid) + otx.AddFlow("table=9, reg0=%d, priority=1, actions=drop", vnid) + } else /* vnid != 0 && len(policies) == 1 */ { + // Temporarily drop all outgoing traffic, to avoid race conditions while modifying the other rules + otx.AddFlow("table=9, reg0=%d, cookie=1, priority=65535, actions=drop", vnid) + otx.DeleteFlows("table=9, reg0=%d, cookie=0/1", vnid) + + for i, rule := range policies[0].Spec.Egress { + priority := len(policies[0].Spec.Egress) - i + + var action string + if rule.Type == osapi.EgressNetworkPolicyRuleAllow { + action = "output:2" + } else { + action = "drop" + } + + var dst string + if rule.To.CIDRSelector == "0.0.0.0/32" { + dst = "" + } else { + dst = fmt.Sprintf(", nw_dst=%s", rule.To.CIDRSelector) + } + + otx.AddFlow("table=9, reg0=%d, priority=%d, ip%s, actions=%s", vnid, priority, dst, action) + } + otx.DeleteFlows("table=9, reg0=%d, cookie=1/1", vnid) + } + + err := otx.EndTransaction() + if err != nil { + return fmt.Errorf("Error updating OVS flows for EgressNetworkPolicy: %v", err) + } + return nil +} + func (plugin *OsdnNode) AddHostSubnetRules(subnet *osapi.HostSubnet) error { glog.Infof("AddHostSubnetRules for %s", hostSubnetToString(subnet)) otx := ovs.NewTransaction(kexec.New(), BR) diff --git a/pkg/sdn/plugin/node.go b/pkg/sdn/plugin/node.go index 2e3c3af4796b..bdca07feb1f4 100644 --- a/pkg/sdn/plugin/node.go +++ b/pkg/sdn/plugin/node.go @@ -30,6 +30,7 @@ type OsdnNode struct { vnids *vnidMap iptablesSyncPeriod time.Duration mtu uint32 + egressPolicies map[uint32][]*osapi.EgressNetworkPolicy } // Called by higher layers to create the plugin SDN node instance @@ -71,6 +72,7 @@ func NewNodePlugin(pluginName string, osClient *osclient.Client, kClient *kclien podNetworkReady: make(chan struct{}), iptablesSyncPeriod: iptablesSyncPeriod, mtu: mtu, + egressPolicies: make(map[uint32][]*osapi.EgressNetworkPolicy), } return plugin, nil } @@ -96,6 +98,9 @@ func (node *OsdnNode) Start() error { if err = node.VnidStartNode(); err != nil { return err } + if err = node.SetupEgressNetworkPolicy(); err != nil { + return err + } } if networkChanged { diff --git a/pkg/sdn/plugin/proxy.go b/pkg/sdn/plugin/proxy.go index 144240631f60..c3c13a459b86 100644 --- a/pkg/sdn/plugin/proxy.go +++ b/pkg/sdn/plugin/proxy.go @@ -9,6 +9,7 @@ import ( "github.com/golang/glog" osclient "github.com/openshift/origin/pkg/client" + osapi "github.com/openshift/origin/pkg/sdn/api" "github.com/openshift/origin/pkg/sdn/plugin/api" kapi "k8s.io/kubernetes/pkg/api" @@ -19,10 +20,16 @@ import ( "k8s.io/kubernetes/pkg/watch" ) +type proxyFirewallItem struct { + policy osapi.EgressNetworkPolicyRuleType + net *net.IPNet +} + type ovsProxyPlugin struct { registry *Registry podsByIP map[string]*kapi.Pod podsMutex sync.Mutex + firewall map[string][]proxyFirewallItem baseEndpointsHandler pconfig.EndpointsConfigHandler } @@ -36,6 +43,7 @@ func NewProxyPlugin(pluginName string, osClient *osclient.Client, kClient *kclie return &ovsProxyPlugin{ registry: newRegistry(osClient, kClient), podsByIP: make(map[string]*kapi.Pod), + firewall: make(map[string][]proxyFirewallItem), }, nil } @@ -50,15 +58,61 @@ func (proxy *ovsProxyPlugin) Start(baseHandler pconfig.EndpointsConfigHandler) e return err } + policies, err := proxy.registry.GetEgressNetworkPolicies() + if err != nil { + return fmt.Errorf("Could not get EgressNetworkPolicies: %s", err) + } + for _, policy := range policies { + proxy.updateNetworkPolicy(policy) + } + for _, pod := range pods { proxy.trackPod(&pod) } go utilwait.Forever(proxy.watchPods, 0) + go utilwait.Forever(proxy.watchEgressNetworkPolicies, 0) return nil } +func (proxy *ovsProxyPlugin) watchEgressNetworkPolicies() { + eventQueue := proxy.registry.RunEventQueue(EgressNetworkPolicies) + + for { + eventType, obj, err := eventQueue.Pop() + if err != nil { + utilruntime.HandleError(fmt.Errorf("EventQueue failed for EgressNetworkPolicy: %v", err)) + return + } + policy := obj.(*osapi.EgressNetworkPolicy) + if eventType == watch.Deleted { + policy.Spec.Egress = nil + } + proxy.updateNetworkPolicy(*policy) + // FIXME: poke the endpoint-syncer somehow... + } +} + +func (proxy *ovsProxyPlugin) updateNetworkPolicy(policy osapi.EgressNetworkPolicy) { + firewall := make([]proxyFirewallItem, len(policy.Spec.Egress)) + for i, rule := range policy.Spec.Egress { + _, cidr, err := net.ParseCIDR(rule.To.CIDRSelector) + if err != nil { + // should have been caught by validation + glog.Errorf("Illegal CIDR value %q in EgressNetworkPolicy rule", rule.To.CIDRSelector) + return + } + firewall[i] = proxyFirewallItem{rule.Type, cidr} + } + + if len(firewall) > 0 { + proxy.firewall[policy.Namespace] = firewall + } else { + delete(proxy.firewall, policy.Namespace) + } +} + func (proxy *ovsProxyPlugin) watchPods() { eventQueue := proxy.registry.RunEventQueue(Pods) @@ -127,6 +181,15 @@ func (proxy *ovsProxyPlugin) unTrackPod(pod *kapi.Pod) { } } +func (proxy *ovsProxyPlugin) firewallBlocksIP(namespace string, ip net.IP) bool { + for _, item := range proxy.firewall[namespace] { + if item.net.Contains(ip) { + return item.policy == osapi.EgressNetworkPolicyRuleDeny + } + } + return false +} + func (proxy *ovsProxyPlugin) OnEndpointsUpdate(allEndpoints []kapi.Endpoints) { ni, err := proxy.registry.GetNetworkInfo() if err != nil { @@ -145,8 +208,7 @@ EndpointLoop: if ni.ServiceNetwork.Contains(IP) { glog.Warningf("Service '%s' in namespace '%s' has an Endpoint inside the service network (%s)", ep.ObjectMeta.Name, ns, addr.IP) continue EndpointLoop - } - if ni.ClusterNetwork.Contains(IP) { + } else if ni.ClusterNetwork.Contains(IP) { podInfo, ok := proxy.getTrackedPod(addr.IP) if !ok { glog.Warningf("Service '%s' in namespace '%s' has an Endpoint pointing to non-existent pod (%s)", ep.ObjectMeta.Name, ns, addr.IP) @@ -156,6 +218,11 @@ EndpointLoop: glog.Warningf("Service '%s' in namespace '%s' has an Endpoint pointing to pod %s in namespace '%s'", ep.ObjectMeta.Name, ns, addr.IP, podInfo.ObjectMeta.Namespace) continue EndpointLoop } + } else { + if proxy.firewallBlocksIP(ns, IP) { + glog.Warningf("Service '%s' in namespace '%s' has an Endpoint pointing to firewalled destination (%s)", ep.ObjectMeta.Name, ns, addr.IP) + continue EndpointLoop + } } } } diff --git a/pkg/sdn/plugin/registry.go b/pkg/sdn/plugin/registry.go index da3e68c40c0b..7c78e39c0343 100644 --- a/pkg/sdn/plugin/registry.go +++ b/pkg/sdn/plugin/registry.go @@ -38,12 +38,13 @@ type Registry struct { type ResourceName string const ( - Nodes ResourceName = "Nodes" - Namespaces ResourceName = "Namespaces" - NetNamespaces ResourceName = "NetNamespaces" - Services ResourceName = "Services" - HostSubnets ResourceName = "HostSubnets" - Pods ResourceName = "Pods" + Nodes ResourceName = "Nodes" + Namespaces ResourceName = "Namespaces" + NetNamespaces ResourceName = "NetNamespaces" + Services ResourceName = "Services" + HostSubnets ResourceName = "HostSubnets" + Pods ResourceName = "Pods" + EgressNetworkPolicies ResourceName = "EgressNetworkPolicies" ) func newRegistry(osClient *osclient.Client, kClient *kclient.Client) *Registry { @@ -259,6 +260,14 @@ func (registry *Registry) getServices(namespace string) ([]kapi.Service, error) return servList, nil } +func (registry *Registry) GetEgressNetworkPolicies() ([]osapi.EgressNetworkPolicy, error) { + policyList, err := registry.oClient.EgressNetworkPolicies(kapi.NamespaceAll).List(kapi.ListOptions{}) + if err != nil { + return nil, err + } + return policyList.Items, nil +} + // Run event queue for the given resource func (registry *Registry) RunEventQueue(resourceName ResourceName) *oscache.EventQueue { var client cache.Getter @@ -283,6 +292,9 @@ func (registry *Registry) RunEventQueue(resourceName ResourceName) *oscache.Even case Pods: expectedType = &kapi.Pod{} client = registry.kClient + case EgressNetworkPolicies: + expectedType = &osapi.EgressNetworkPolicy{} + client = registry.oClient default: log.Fatalf("Unknown resource %s during initialization of event queue", resourceName) } diff --git a/pkg/sdn/plugin/vnids.go b/pkg/sdn/plugin/vnids.go index 8b3834a0dc6b..123a07a92f65 100644 --- a/pkg/sdn/plugin/vnids.go +++ b/pkg/sdn/plugin/vnids.go @@ -28,12 +28,34 @@ const ( ) type vnidMap struct { - ids map[string]uint32 - lock sync.Mutex + ids map[string]uint32 + namespaces map[uint32]sets.String + lock sync.Mutex } func newVnidMap() *vnidMap { - return &vnidMap{ids: make(map[string]uint32)} + return &vnidMap{ + ids: make(map[string]uint32), + namespaces: make(map[uint32]sets.String), + } +} + +func (vmap *vnidMap) addNamespaceToSet(name string, vnid uint32) { + set, found := vmap.namespaces[vnid] + if !found { + set = sets.NewString() + vmap.namespaces[vnid] = set + } + set.Insert(name) +} + +func (vmap *vnidMap) removeNamespaceFromSet(name string, vnid uint32) { + if set, found := vmap.namespaces[vnid]; found { + set.Delete(name) + if set.Len() == 0 { + delete(vmap.namespaces, vnid) + } + } } func (vmap *vnidMap) GetVNID(name string) (uint32, error) { @@ -68,11 +90,27 @@ func (vmap *vnidMap) WaitAndGetVNID(name string) (uint32, error) { return MaxVNID + 1, fmt.Errorf("Failed to find netid for namespace: %s in vnid map", name) } +func (vmap *vnidMap) GetNamespaces(id uint32) []string { + vmap.lock.Lock() + defer vmap.lock.Unlock() + + if set, ok := vmap.namespaces[id]; ok { + return set.List() + } else { + return nil + } +} + func (vmap *vnidMap) SetVNID(name string, id uint32) { vmap.lock.Lock() defer vmap.lock.Unlock() + if oldId, found := vmap.ids[name]; found { + vmap.removeNamespaceFromSet(name, oldId) + } vmap.ids[name] = id + vmap.addNamespaceToSet(name, id) + log.Infof("Associate netid %d to namespace %q", id, name) } @@ -85,6 +123,7 @@ func (vmap *vnidMap) UnsetVNID(name string) (id uint32, err error) { // In case of error, return some value which is not a valid VNID return MaxVNID + 1, fmt.Errorf("Failed to find netid for namespace: %s in vnid map", name) } + vmap.removeNamespaceFromSet(name, id) delete(vmap.ids, name) log.Infof("Dissociate netid %d from namespace %q", id, name) return id, nil @@ -94,12 +133,8 @@ func (vmap *vnidMap) CheckVNID(id uint32) bool { vmap.lock.Lock() defer vmap.lock.Unlock() - for _, netid := range vmap.ids { - if netid == id { - return true - } - } - return false + _, found := vmap.namespaces[id] + return found } func (vmap *vnidMap) GetAllocatedVNIDs() []uint32 { @@ -275,25 +310,31 @@ func (node *OsdnNode) VnidStartNode() error { return nil } -func (node *OsdnNode) updatePodNetwork(namespace string, netID uint32) error { - // Update OF rules for the existing/old pods in the namespace +func (node *OsdnNode) updatePodNetwork(namespace string, oldNetID, netID uint32) error { + // FIXME: this is racy; traffic coming from the pods gets switched to the new + // VNID before the service and firewall rules are updated to match. We need + // to do the updates as a single transaction (ovs-ofctl --bundle). + pods, err := node.GetLocalPods(namespace) if err != nil { return err } + services, err := node.registry.GetServicesForNamespace(namespace) + if err != nil { + return err + } + + errList := []error{} + + // Update OF rules for the existing/old pods in the namespace for _, pod := range pods { err = node.UpdatePod(pod.Namespace, pod.Name, kubetypes.DockerID(getPodContainerID(&pod))) if err != nil { - return err + errList = append(errList, err) } } // Update OF rules for the old services in the namespace - services, err := node.registry.GetServicesForNamespace(namespace) - if err != nil { - return err - } - errList := []error{} for _, svc := range services { if err = node.DeleteServiceRules(&svc); err != nil { log.Error(err) @@ -302,6 +343,12 @@ func (node *OsdnNode) updatePodNetwork(namespace string, netID uint32) error { errList = append(errList, err) } } + + // Update namespace references in egress firewall rules + if err = node.UpdateEgressNetworkPolicyVNID(namespace, oldNetID, netID); err != nil { + errList = append(errList, err) + } + return kerrors.NewAggregate(errList) } @@ -327,7 +374,7 @@ func (node *OsdnNode) watchNetNamespaces() { } node.vnids.SetVNID(netns.NetName, netns.NetID) - err = node.updatePodNetwork(netns.NetName, netns.NetID) + err = node.updatePodNetwork(netns.NetName, oldNetID, netns.NetID) if err != nil { log.Errorf("Failed to update pod network for namespace '%s', error: %s", netns.NetName, err) node.vnids.SetVNID(netns.NetName, oldNetID) @@ -335,7 +382,7 @@ func (node *OsdnNode) watchNetNamespaces() { } case watch.Deleted: // updatePodNetwork needs vnid, so unset vnid after this call - err = node.updatePodNetwork(netns.NetName, AdminVNID) + err = node.updatePodNetwork(netns.NetName, netns.NetID, AdminVNID) if err != nil { log.Errorf("Failed to update pod network for namespace '%s', error: %s", netns.NetName, err) } diff --git a/pkg/sdn/plugin/vnids_test.go b/pkg/sdn/plugin/vnids_test.go new file mode 100644 index 000000000000..4072ddb4a3ec --- /dev/null +++ b/pkg/sdn/plugin/vnids_test.go @@ -0,0 +1,196 @@ +package plugin + +import ( + "testing" +) + +func TestVnidMap(t *testing.T) { + vmap := newVnidMap() + + // empty vmap + + checkNotExists(t, vmap, "alpha") + checkNamespaces(t, vmap, 1, []string{}) + checkAllocatedVNIDs(t, vmap, []uint32{}) + + // set vnids, non-overlapping + + vmap.SetVNID("alpha", 1) + vmap.SetVNID("bravo", 2) + vmap.SetVNID("charlie", 3) + vmap.SetVNID("delta", 4) + + checkExists(t, vmap, "alpha", 1) + checkExists(t, vmap, "bravo", 2) + checkExists(t, vmap, "charlie", 3) + checkExists(t, vmap, "delta", 4) + checkNotExists(t, vmap, "echo") + + checkNamespaces(t, vmap, 1, []string{"alpha"}) + checkNamespaces(t, vmap, 2, []string{"bravo"}) + checkNamespaces(t, vmap, 3, []string{"charlie"}) + checkNamespaces(t, vmap, 4, []string{"delta"}) + + checkAllocatedVNIDs(t, vmap, []uint32{1, 2, 3, 4}) + + // unset vnids + + id, err := vmap.UnsetVNID("alpha") + if id != 1 || err != nil { + t.Fatalf("Unexpected failure: %d, %v", id, err) + } + id, err = vmap.UnsetVNID("charlie") + if id != 3 || err != nil { + t.Fatalf("Unexpected failure: %d, %v", id, err) + } + + checkNotExists(t, vmap, "alpha") + checkExists(t, vmap, "bravo", 2) + checkNotExists(t, vmap, "charlie") + checkExists(t, vmap, "delta", 4) + + id, err = vmap.UnsetVNID("alpha") + if err == nil { + t.Fatalf("Unexpected success: %d", id) + } + id, err = vmap.UnsetVNID("echo") + if err == nil { + t.Fatalf("Unexpected success: %d", id) + } + + checkNamespaces(t, vmap, 1, []string{}) + checkNamespaces(t, vmap, 2, []string{"bravo"}) + checkNamespaces(t, vmap, 3, []string{}) + checkNamespaces(t, vmap, 4, []string{"delta"}) + + checkAllocatedVNIDs(t, vmap, []uint32{2, 4}) + + // change vnids + + vmap.SetVNID("bravo", 1) + vmap.SetVNID("delta", 2) + + checkExists(t, vmap, "bravo", 1) + checkExists(t, vmap, "delta", 2) + + checkNamespaces(t, vmap, 1, []string{"bravo"}) + checkNamespaces(t, vmap, 2, []string{"delta"}) + checkNamespaces(t, vmap, 3, []string{}) + checkNamespaces(t, vmap, 4, []string{}) + + checkAllocatedVNIDs(t, vmap, []uint32{1, 2}) + + // overlapping vnids + + vmap.SetVNID("echo", 3) + vmap.SetVNID("foxtrot", 5) + vmap.SetVNID("golf", 1) + vmap.SetVNID("hotel", 1) + vmap.SetVNID("india", 1) + vmap.SetVNID("juliet", 3) + + checkExists(t, vmap, "bravo", 1) + checkExists(t, vmap, "delta", 2) + checkExists(t, vmap, "echo", 3) + checkExists(t, vmap, "foxtrot", 5) + checkExists(t, vmap, "golf", 1) + checkExists(t, vmap, "hotel", 1) + checkExists(t, vmap, "india", 1) + checkExists(t, vmap, "juliet", 3) + + checkNamespaces(t, vmap, 1, []string{"bravo", "golf", "hotel", "india"}) + checkNamespaces(t, vmap, 2, []string{"delta"}) + checkNamespaces(t, vmap, 3, []string{"echo", "juliet"}) + checkNamespaces(t, vmap, 4, []string{}) + checkNamespaces(t, vmap, 5, []string{"foxtrot"}) + + checkAllocatedVNIDs(t, vmap, []uint32{1, 2, 3, 5}) + + // deleting with overlapping vnids + + id, err = vmap.UnsetVNID("golf") + if err != nil { + t.Fatalf("Unexpected failure: %d, %v", id, err) + } + id, err = vmap.UnsetVNID("echo") + if err != nil { + t.Fatalf("Unexpected failure: %d, %v", id, err) + } + id, err = vmap.UnsetVNID("juliet") + if err != nil { + t.Fatalf("Unexpected failure: %d, %v", id, err) + } + + checkExists(t, vmap, "bravo", 1) + checkExists(t, vmap, "delta", 2) + checkNotExists(t, vmap, "echo") + checkExists(t, vmap, "foxtrot", 5) + checkNotExists(t, vmap, "golf") + checkExists(t, vmap, "hotel", 1) + checkExists(t, vmap, "india", 1) + checkNotExists(t, vmap, "juliet") + + checkNamespaces(t, vmap, 1, []string{"bravo", "hotel", "india"}) + checkNamespaces(t, vmap, 2, []string{"delta"}) + checkNamespaces(t, vmap, 3, []string{}) + checkNamespaces(t, vmap, 4, []string{}) + checkNamespaces(t, vmap, 5, []string{"foxtrot"}) + + checkAllocatedVNIDs(t, vmap, []uint32{1, 2, 5}) + +} + +func checkExists(t *testing.T, vmap *vnidMap, name string, expected uint32) { + id, err := vmap.GetVNID(name) + if id != expected || err != nil { + t.Fatalf("Unexpected failure: %d, %v", id, err) + } + if !vmap.CheckVNID(id) { + t.Fatalf("Unexpected failure") + } +} + +func checkNotExists(t *testing.T, vmap *vnidMap, name string) { + id, err := vmap.GetVNID(name) + if err == nil { + t.Fatalf("Unexpected success: %d", id) + } +} + +func checkNamespaces(t *testing.T, vmap *vnidMap, vnid uint32, match []string) { + namespaces := vmap.GetNamespaces(vnid) + if len(namespaces) != len(match) { + t.Fatalf("Wrong number of namespaces: %v vs %v", namespaces, match) + } + for _, m := range match { + found := false + for _, n := range namespaces { + if n == m { + found = true + break + } + } + if !found { + t.Fatalf("Missing namespace: %s", m) + } + } +} + +func checkAllocatedVNIDs(t *testing.T, vmap *vnidMap, match []uint32) { + vnids := vmap.GetAllocatedVNIDs() + if len(vnids) != len(match) { + t.Fatalf("Wrong number of VNIDs: %v vs %v", vnids, match) + } + for _, m := range match { + found := false + for _, n := range vnids { + if n == m { + found = true + break + } + } + if !found { + t.Fatalf("Missing VNID: %d", m) + } + } +} diff --git a/pkg/sdn/registry/egressnetworkpolicy/etcd/etcd.go b/pkg/sdn/registry/egressnetworkpolicy/etcd/etcd.go new file mode 100644 index 000000000000..bb9253775133 --- /dev/null +++ b/pkg/sdn/registry/egressnetworkpolicy/etcd/etcd.go @@ -0,0 +1,51 @@ +package etcd + +import ( + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/registry/generic" + "k8s.io/kubernetes/pkg/registry/generic/registry" + "k8s.io/kubernetes/pkg/runtime" + + "github.com/openshift/origin/pkg/sdn/api" + "github.com/openshift/origin/pkg/sdn/registry/egressnetworkpolicy" + "github.com/openshift/origin/pkg/util/restoptions" +) + +// rest implements a RESTStorage for egress network policy against etcd +type REST struct { + registry.Store +} + +const etcdPrefix = "/registry/egressnetworkpolicy" + +// NewREST returns a RESTStorage object that will work against egress network policy +func NewREST(optsGetter restoptions.Getter) (*REST, error) { + store := ®istry.Store{ + NewFunc: func() runtime.Object { return &api.EgressNetworkPolicy{} }, + NewListFunc: func() runtime.Object { return &api.EgressNetworkPolicyList{} }, + KeyRootFunc: func(ctx kapi.Context) string { + return registry.NamespaceKeyRootFunc(ctx, etcdPrefix) + }, + KeyFunc: func(ctx kapi.Context, name string) (string, error) { + return registry.NamespaceKeyFunc(ctx, etcdPrefix, name) + }, + ObjectNameFunc: func(obj runtime.Object) (string, error) { + return obj.(*api.EgressNetworkPolicy).Name, nil + }, + PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { + return egressnetworkpolicy.Matcher(label, field) + }, + QualifiedResource: api.Resource("egressnetworkpolicies"), + + CreateStrategy: egressnetworkpolicy.Strategy, + UpdateStrategy: egressnetworkpolicy.Strategy, + } + + if err := restoptions.ApplyOptions(optsGetter, store, etcdPrefix); err != nil { + return nil, err + } + + return &REST{*store}, nil +} diff --git a/pkg/sdn/registry/egressnetworkpolicy/strategy.go b/pkg/sdn/registry/egressnetworkpolicy/strategy.go new file mode 100644 index 000000000000..1b6fcc4811f0 --- /dev/null +++ b/pkg/sdn/registry/egressnetworkpolicy/strategy.go @@ -0,0 +1,73 @@ +package egressnetworkpolicy + +import ( + "fmt" + + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/fields" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/registry/generic" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/validation/field" + + "github.com/openshift/origin/pkg/sdn/api" + "github.com/openshift/origin/pkg/sdn/api/validation" +) + +// enpStrategy implements behavior for EgressNetworkPolicies +type enpStrategy struct { + runtime.ObjectTyper +} + +// Strategy is the default logic that applies when creating and updating EgressNetworkPolicy +// objects via the REST API. +var Strategy = enpStrategy{kapi.Scheme} + +func (enpStrategy) PrepareForUpdate(obj, old runtime.Object) {} + +// NamespaceScoped is true for egress network policy +func (enpStrategy) NamespaceScoped() bool { + return true +} + +func (enpStrategy) GenerateName(base string) string { + return base +} + +func (enpStrategy) PrepareForCreate(obj runtime.Object) { +} + +// Canonicalize normalizes the object after validation. +func (enpStrategy) Canonicalize(obj runtime.Object) { +} + +// Validate validates a new egress network policy +func (enpStrategy) Validate(ctx kapi.Context, obj runtime.Object) field.ErrorList { + return validation.ValidateEgressNetworkPolicy(obj.(*api.EgressNetworkPolicy)) +} + +// AllowCreateOnUpdate is false for egress network policies +func (enpStrategy) AllowCreateOnUpdate() bool { + return false +} + +func (enpStrategy) AllowUnconditionalUpdate() bool { + return false +} + +// ValidateUpdate is the default update validation for a EgressNetworkPolicy +func (enpStrategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) field.ErrorList { + return validation.ValidateEgressNetworkPolicyUpdate(obj.(*api.EgressNetworkPolicy), old.(*api.EgressNetworkPolicy)) +} + +// Matcher returns a generic matcher for a given label and field selector. +func Matcher(label labels.Selector, field fields.Selector) generic.Matcher { + return generic.MatcherFunc(func(obj runtime.Object) (bool, error) { + network, ok := obj.(*api.EgressNetworkPolicy) + if !ok { + return false, fmt.Errorf("not an EgressNetworkPolicy") + } + fields := api.EgressNetworkPolicyToSelectableFields(network) + return label.Matches(labels.Set(network.Labels)) && field.Matches(fields), nil + }) +} diff --git a/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml b/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml index 05045baeaf9f..3d6b61a220a7 100644 --- a/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml +++ b/test/testdata/bootstrappolicy/bootstrap_cluster_roles.yaml @@ -48,6 +48,7 @@ items: - bindings - componentstatuses - configmaps + - egressnetworkpolicies - endpoints - events - limitranges @@ -1887,6 +1888,15 @@ items: - get - list - watch + - apiGroups: + - "" + attributeRestrictions: null + resources: + - egressnetworkpolicies + verbs: + - get + - list + - watch - apiGroups: - "" attributeRestrictions: null