Merge pull request #2012 from duanjiong/network-policy

Network policy
This commit is contained in:
KubeSphere CI Bot
2020-04-29 10:44:27 +08:00
committed by GitHub
381 changed files with 4100 additions and 68577 deletions

View File

@@ -1,4 +0,0 @@
FROM gcr.io/distroless/static:latest
WORKDIR /
COPY ks-network .
ENTRYPOINT ["/ks-network"]

View File

@@ -25,6 +25,8 @@ import (
"kubesphere.io/kubesphere/pkg/controller/devopscredential"
"kubesphere.io/kubesphere/pkg/controller/devopsproject"
"kubesphere.io/kubesphere/pkg/controller/job"
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
"kubesphere.io/kubesphere/pkg/controller/pipeline"
"kubesphere.io/kubesphere/pkg/controller/s2ibinary"
"kubesphere.io/kubesphere/pkg/controller/s2irun"
@@ -126,6 +128,17 @@ func AddControllers(
kubesphereInformer.Cluster().V1alpha1().Clusters(),
client.KubeSphere().ClusterV1alpha1().Clusters())
nsnpProvider, err := provider.NewNsNetworkPolicyProvider(client.Kubernetes(),
kubernetesInformer.Networking().V1().NetworkPolicies())
if err != nil {
return err
}
nsnpController := nsnetworkpolicy.NewNSNetworkPolicyController(client.Kubernetes(),
client.KubeSphere().NetworkV1alpha1(), kubesphereInformer.Network().V1alpha1().NamespaceNetworkPolicies(),
kubernetesInformer.Core().V1().Services(), kubernetesInformer.Core().V1().Nodes(),
kubesphereInformer.Tenant().V1alpha1().Workspaces(),
kubernetesInformer.Core().V1().Namespaces(), nsnpProvider)
controllers := map[string]manager.Runnable{
"virtualservice-controller": vsController,
"destinationrule-controller": drController,
@@ -137,8 +150,9 @@ func AddControllers(
"devopsprojects-controller": devopsProjectController,
"pipeline-controller": devopsPipelineController,
"devopscredential-controller": devopsCredentialController,
"cluster-controller": clusterController,
"user-controller": userController,
"cluster-controller": clusterController,
"nsnp-controller": nsnpController,
}
for name, ctrl := range controllers {

View File

@@ -35,6 +35,7 @@ import (
controllerconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
"kubesphere.io/kubesphere/pkg/controller/namespace"
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
"kubesphere.io/kubesphere/pkg/controller/user"
"kubesphere.io/kubesphere/pkg/controller/workspace"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
@@ -160,6 +161,7 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
klog.Info("registering webhooks to the webhook server")
hookServer.Register("/mutating-encrypt-password-iam-kubesphere-io-v1alpha2-user", &webhook.Admission{Handler: &user.PasswordCipher{Client: mgr.GetClient()}})
hookServer.Register("/validate-email-iam-kubesphere-io-v1alpha2-user", &webhook.Admission{Handler: &user.EmailValidator{Client: mgr.GetClient()}})
hookServer.Register("/validate-service-nsnp-kubesphere-io-v1alpha1-network", &webhook.Admission{Handler: &nsnetworkpolicy.ServiceValidator{}})
klog.V(0).Info("Starting the controllers.")
if err = mgr.Start(stopCh); err != nil {

View File

@@ -1,25 +0,0 @@
package main
import (
"flag"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/controller/network/runoption"
)
var opt runoption.RunOption
func init() {
flag.StringVar(&opt.ProviderName, "np-provider", "calico", "specify the network policy provider, k8s or calico")
flag.BoolVar(&opt.AllowInsecureEtcd, "allow-insecure-etcd", false, "specify allow connect to etcd using insecure http")
flag.StringVar(&opt.DataStoreType, "datastore-type", "k8s", "specify the datastore type of calico")
//TODO add more flags
}
func main() {
klog.InitFlags(nil)
flag.Set("logtostderr", "true")
flag.Parse()
klog.V(1).Info("Preparing kubernetes client")
klog.Fatal(opt.Run())
}

View File

@@ -1,535 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: workspacenetworkpolicies.network.kubesphere.io
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: WorkspaceNetworkPolicy
listKind: WorkspaceNetworkPolicyList
plural: workspacenetworkpolicies
shortNames:
- wsnp
singular: workspacenetworkpolicy
scope: Cluster
validation:
openAPIV3Schema:
description: WorkspaceNetworkPolicy is a set of network policies applied to
the scope to workspace
properties:
apiVersion:
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy
properties:
egress:
description: List of egress rules to be applied to the selected pods.
Outgoing traffic is allowed if there are no NetworkPolicies selecting
the pod (and cluster policy otherwise allows the traffic), OR if the
traffic matches at least one egress rule across all of the NetworkPolicy
objects whose podSelector matches the pod. If this field is empty
then this NetworkPolicy limits all outgoing traffic (and serves solely
to ensure that the pods it selects are isolated by default). This
field is beta-level in 1.8
items:
description: WorkspaceNetworkPolicyEgressRule describes a particular
set of traffic that is allowed out of pods matched by a WorkspaceNetworkPolicySpec's
podSelector. The traffic must match both ports and to.
properties:
from:
description: List of sources which should be able to access the
pods selected for this rule. Items in this list are combined
using a logical OR operation. If this field is empty or missing,
this rule matches all sources (traffic not restricted by source).
If this field is present and contains at least on item, this
rule allows traffic only if the traffic matches at least one
item in the from list.
items:
description: WorkspaceNetworkPolicyPeer describes a peer to
allow traffic from. Only certain combinations of fields are
allowed. It is same as 'NetworkPolicyPeer' in k8s but with
an additional field 'WorkspaceSelector'
properties:
ipBlock:
description: IPBlock defines policy on a particular IPBlock.
If this field is set then neither of the other fields
can be.
properties:
cidr:
description: CIDR is a string representing the IP Block
Valid examples are "192.168.1.1/24"
type: string
except:
description: Except is a slice of CIDRs that should
not be included within an IP Block Valid examples
are "192.168.1.1/24" Except values will be rejected
if they are outside the CIDR range
items:
type: string
type: array
required:
- cidr
type: object
namespaceSelector:
description: "Selects Namespaces using cluster-scoped labels.
This field follows standard label selector semantics;
if present but empty, it selects all namespaces. \n If
PodSelector is also set, then the NetworkPolicyPeer as
a whole selects the Pods matching PodSelector in the Namespaces
selected by NamespaceSelector. Otherwise it selects all
Pods in the Namespaces selected by NamespaceSelector."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
podSelector:
description: "This is a label selector which selects Pods.
This field follows standard label selector semantics;
if present but empty, it selects all pods. \n If NamespaceSelector
is also set, then the NetworkPolicyPeer as a whole selects
the Pods matching PodSelector in the Namespaces selected
by NamespaceSelector. Otherwise it selects the Pods matching
PodSelector in the policy's own Namespace."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
workspaceSelector:
description: A label selector is a label query over a set
of resources. The result of matchLabels and matchExpressions
are ANDed. An empty label selector matches all objects.
A null label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
type: object
type: array
ports:
description: List of ports which should be made accessible on
the pods selected for this rule. Each item in this list is combined
using a logical OR. If this field is empty or missing, this
rule matches all ports (traffic not restricted by port). If
this field is present and contains at least one item, then this
rule allows traffic only if the traffic matches at least one
port in the list.
items:
description: NetworkPolicyPort describes a port to allow traffic
on
properties:
port:
anyOf:
- type: integer
- type: string
description: The port on the given protocol. This can either
be a numerical or named port on a pod. If this field is
not provided, this matches all port names and numbers.
x-kubernetes-int-or-string: true
protocol:
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to TCP.
type: string
type: object
type: array
type: object
type: array
ingress:
description: List of ingress rules to be applied to the selected pods.
Traffic is allowed to a pod if there are no NetworkPolicies selecting
the pod (and cluster policy otherwise allows the traffic), OR if the
traffic source is the pod's local node, OR if the traffic matches
at least one ingress rule across all of the NetworkPolicy objects
whose podSelector matches the pod. If this field is empty then this
NetworkPolicy does not allow any traffic (and serves solely to ensure
that the pods it selects are isolated by default)
items:
description: WorkspaceNetworkPolicyIngressRule describes a particular
set of traffic that is allowed to the pods matched by a WorkspaceNetworkPolicySpec's
podSelector. The traffic must match both ports and from.
properties:
from:
description: List of sources which should be able to access the
pods selected for this rule. Items in this list are combined
using a logical OR operation. If this field is empty or missing,
this rule matches all sources (traffic not restricted by source).
If this field is present and contains at least on item, this
rule allows traffic only if the traffic matches at least one
item in the from list.
items:
description: WorkspaceNetworkPolicyPeer describes a peer to
allow traffic from. Only certain combinations of fields are
allowed. It is same as 'NetworkPolicyPeer' in k8s but with
an additional field 'WorkspaceSelector'
properties:
ipBlock:
description: IPBlock defines policy on a particular IPBlock.
If this field is set then neither of the other fields
can be.
properties:
cidr:
description: CIDR is a string representing the IP Block
Valid examples are "192.168.1.1/24"
type: string
except:
description: Except is a slice of CIDRs that should
not be included within an IP Block Valid examples
are "192.168.1.1/24" Except values will be rejected
if they are outside the CIDR range
items:
type: string
type: array
required:
- cidr
type: object
namespaceSelector:
description: "Selects Namespaces using cluster-scoped labels.
This field follows standard label selector semantics;
if present but empty, it selects all namespaces. \n If
PodSelector is also set, then the NetworkPolicyPeer as
a whole selects the Pods matching PodSelector in the Namespaces
selected by NamespaceSelector. Otherwise it selects all
Pods in the Namespaces selected by NamespaceSelector."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
podSelector:
description: "This is a label selector which selects Pods.
This field follows standard label selector semantics;
if present but empty, it selects all pods. \n If NamespaceSelector
is also set, then the NetworkPolicyPeer as a whole selects
the Pods matching PodSelector in the Namespaces selected
by NamespaceSelector. Otherwise it selects the Pods matching
PodSelector in the policy's own Namespace."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
workspaceSelector:
description: A label selector is a label query over a set
of resources. The result of matchLabels and matchExpressions
are ANDed. An empty label selector matches all objects.
A null label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
type: object
type: array
ports:
description: List of ports which should be made accessible on
the pods selected for this rule. Each item in this list is combined
using a logical OR. If this field is empty or missing, this
rule matches all ports (traffic not restricted by port). If
this field is present and contains at least one item, then this
rule allows traffic only if the traffic matches at least one
port in the list.
items:
description: NetworkPolicyPort describes a port to allow traffic
on
properties:
port:
anyOf:
- type: integer
- type: string
description: The port on the given protocol. This can either
be a numerical or named port on a pod. If this field is
not provided, this matches all port names and numbers.
x-kubernetes-int-or-string: true
protocol:
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to TCP.
type: string
type: object
type: array
type: object
type: array
policyTypes:
description: List of rule types that the WorkspaceNetworkPolicy relates
to. Valid options are Ingress, Egress, or Ingress,Egress. If this
field is not specified, it will default based on the existence of
Ingress or Egress rules; policies that contain an Egress section are
assumed to affect Egress, and all policies (whether or not they contain
an Ingress section) are assumed to affect Ingress. If you want to
write an egress-only policy, you must explicitly specify policyTypes
[ "Egress" ]. Likewise, if you want to write a policy that specifies
that no egress is allowed, you must specify a policyTypes value that
include "Egress" (since such a policy would not include an Egress
section and would otherwise default to just [ "Ingress" ]).
items:
description: Policy Type string describes the NetworkPolicy type This
type is beta-level in 1.8
type: string
type: array
workspace:
description: Workspace specify the name of ws to apply this workspace
network policy
type: string
type: object
status:
description: WorkspaceNetworkPolicyStatus defines the observed state of
WorkspaceNetworkPolicy
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,271 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: namespacenetworkpolicies.network.kubesphere.io
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: NamespaceNetworkPolicy
listKind: NamespaceNetworkPolicyList
plural: namespacenetworkpolicies
shortNames:
- nsnp
singular: namespacenetworkpolicy
scope: Namespaced
validation:
openAPIV3Schema:
description: NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies
API
properties:
apiVersion:
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: NetworkPolicySpec provides the specification of a NetworkPolicy
properties:
egress:
description: List of egress rules to be applied to the selected pods.
Outgoing traffic is allowed if there are no NetworkPolicies selecting
the pod (and cluster policy otherwise allows the traffic), OR if the
traffic matches at least one egress rule across all of the NetworkPolicy
objects whose podSelector matches the pod. If this field is empty
then this NetworkPolicy limits all outgoing traffic (and serves solely
to ensure that the pods it selects are isolated by default). This
field is beta-level in 1.8
items:
description: NetworkPolicyEgressRule describes a particular set of
traffic that is allowed out of pods matched by a NetworkPolicySpec's
podSelector. The traffic must match both ports and to. This type
is beta-level in 1.8
properties:
ports:
description: List of destination ports for outgoing traffic. Each
item in this list is combined using a logical OR. If this field
is empty or missing, this rule matches all ports (traffic not
restricted by port). If this field is present and contains at
least one item, then this rule allows traffic only if the traffic
matches at least one port in the list.
items:
description: NetworkPolicyPort describes a port to allow traffic
on
properties:
port:
anyOf:
- type: integer
- type: string
description: The port on the given protocol. This can either
be a numerical or named port on a pod. If this field is
not provided, this matches all port names and numbers.
x-kubernetes-int-or-string: true
protocol:
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to TCP.
type: string
type: object
type: array
to:
description: List of destinations for outgoing traffic of pods
selected for this rule. Items in this list are combined using
a logical OR operation. If this field is empty or missing, this
rule matches all destinations (traffic not restricted by destination).
If this field is present and contains at least one item, this
rule allows traffic only if the traffic matches at least one
item in the to list.
items:
description: NetworkPolicyPeer describes a peer to allow traffic
from. Only certain combinations of fields are allowed
properties:
ipBlock:
description: IPBlock defines policy on a particular IPBlock.
If this field is set then neither of the other fields
can be.
properties:
cidr:
description: CIDR is a string representing the IP Block
Valid examples are "192.168.1.1/24"
type: string
except:
description: Except is a slice of CIDRs that should
not be included within an IP Block Valid examples
are "192.168.1.1/24" Except values will be rejected
if they are outside the CIDR range
items:
type: string
type: array
required:
- cidr
type: object
namespace:
description: "Selects Namespaces using cluster-scoped labels.
This field follows standard label selector semantics;
if present but empty, it selects all namespaces. \n If
PodSelector is also set, then the NetworkPolicyPeer as
a whole selects the Pods matching PodSelector in the Namespaces
selected by NamespaceSelector. Otherwise it selects all
Pods in the Namespaces selected by NamespaceSelector."
properties:
name:
type: string
required:
- name
type: object
service:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
type: object
type: array
type: object
type: array
ingress:
description: List of ingress rules to be applied to the selected pods.
Traffic is allowed to a pod if there are no NetworkPolicies selecting
the pod (and cluster policy otherwise allows the traffic), OR if the
traffic source is the pod's local node, OR if the traffic matches
at least one ingress rule across all of the NetworkPolicy objects
whose podSelector matches the pod. If this field is empty then this
NetworkPolicy does not allow any traffic (and serves solely to ensure
that the pods it selects are isolated by default)
items:
description: NetworkPolicyIngressRule describes a particular set of
traffic that is allowed to the pods matched by a NetworkPolicySpec's
podSelector. The traffic must match both ports and from.
properties:
from:
description: List of sources which should be able to access the
pods selected for this rule. Items in this list are combined
using a logical OR operation. If this field is empty or missing,
this rule matches all sources (traffic not restricted by source).
If this field is present and contains at least one item, this
rule allows traffic only if the traffic matches at least one
item in the from list.
items:
description: NetworkPolicyPeer describes a peer to allow traffic
from. Only certain combinations of fields are allowed
properties:
ipBlock:
description: IPBlock defines policy on a particular IPBlock.
If this field is set then neither of the other fields
can be.
properties:
cidr:
description: CIDR is a string representing the IP Block
Valid examples are "192.168.1.1/24"
type: string
except:
description: Except is a slice of CIDRs that should
not be included within an IP Block Valid examples
are "192.168.1.1/24" Except values will be rejected
if they are outside the CIDR range
items:
type: string
type: array
required:
- cidr
type: object
namespace:
description: "Selects Namespaces using cluster-scoped labels.
This field follows standard label selector semantics;
if present but empty, it selects all namespaces. \n If
PodSelector is also set, then the NetworkPolicyPeer as
a whole selects the Pods matching PodSelector in the Namespaces
selected by NamespaceSelector. Otherwise it selects all
Pods in the Namespaces selected by NamespaceSelector."
properties:
name:
type: string
required:
- name
type: object
service:
properties:
name:
type: string
namespace:
type: string
required:
- name
- namespace
type: object
type: object
type: array
ports:
description: List of ports which should be made accessible on
the pods selected for this rule. Each item in this list is combined
using a logical OR. If this field is empty or missing, this
rule matches all ports (traffic not restricted by port). If
this field is present and contains at least one item, then this
rule allows traffic only if the traffic matches at least one
port in the list.
items:
description: NetworkPolicyPort describes a port to allow traffic
on
properties:
port:
anyOf:
- type: integer
- type: string
description: The port on the given protocol. This can either
be a numerical or named port on a pod. If this field is
not provided, this matches all port names and numbers.
x-kubernetes-int-or-string: true
protocol:
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to TCP.
type: string
type: object
type: array
type: object
type: array
policyTypes:
description: List of rule types that the NetworkPolicy relates to. Valid
options are "Ingress", "Egress", or "Ingress,Egress". If this field
is not specified, it will default based on the existence of Ingress
or Egress rules; policies that contain an Egress section are assumed
to affect Egress, and all policies (whether or not they contain an
Ingress section) are assumed to affect Ingress. If you want to write
an egress-only policy, you must explicitly specify policyTypes [ "Egress"
]. Likewise, if you want to write a policy that specifies that no
egress is allowed, you must specify a policyTypes value that include
"Egress" (since such a policy would not include an Egress section
and would otherwise default to just [ "Ingress" ]). This field is
beta-level in 1.8
items:
description: Policy Type string describes the NetworkPolicy type This
type is beta-level in 1.8
type: string
type: array
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

24
config/webhook/nsnp.yaml Normal file
View File

@@ -0,0 +1,24 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
creationTimestamp: null
name: kubesphere-nsnp-validate-service
webhooks:
- clientConfig:
caBundle: <caBundle>
service:
name: kubesphere-controller-manager-service
namespace: kubesphere-system
path: /validate-service-nsnp-kubesphere-io-v1alpha1-network
failurePolicy: Fail
name: validate.nsnp.kubesphere.io
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- services

9
go.mod
View File

@@ -30,13 +30,11 @@ require (
github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/structs v1.1.0
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1 // indirect
github.com/go-openapi/loads v0.19.2
github.com/go-openapi/spec v0.19.3
github.com/go-openapi/strfmt v0.19.0
github.com/go-openapi/validate v0.19.2
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-sql-driver/mysql v1.4.1
github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6
@@ -46,7 +44,6 @@ require (
github.com/google/go-cmp v0.3.0
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.1.1
github.com/gophercloud/gophercloud v0.3.0 // indirect
github.com/gorilla/mux v1.7.1 // indirect
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/go-version v1.2.0 // indirect
@@ -56,7 +53,6 @@ require (
github.com/kiali/kiali v0.15.1-0.20191210080139-edbbad1ef779
github.com/kubernetes-sigs/application v0.0.0-20191210100950-18cc93526ab4
github.com/kubesphere/sonargo v0.0.2
github.com/leodido/go-urn v1.1.0 // indirect
github.com/lib/pq v1.2.0 // indirect
github.com/mattn/go-sqlite3 v1.11.0 // indirect
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
@@ -67,7 +63,9 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/openshift/api v0.0.0-20180801171038-322a19404e37 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pkg/errors v0.9.1
github.com/projectcalico/kube-controllers v3.8.8+incompatible
github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce
github.com/prometheus/client_golang v1.0.0
github.com/prometheus/common v0.4.1
@@ -84,7 +82,6 @@ require (
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
google.golang.org/grpc v1.23.1
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
gopkg.in/src-d/go-git.v4 v4.11.0
gopkg.in/yaml.v2 v2.2.8
@@ -294,6 +291,7 @@ replace (
github.com/openshift/api => github.com/openshift/api v0.0.0-20180801171038-322a19404e37
github.com/openshift/generic-admission-server => github.com/openshift/generic-admission-server v1.14.0
github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0
github.com/patrickmn/go-cache => github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pborman/uuid => github.com/pborman/uuid v1.2.0
github.com/pelletier/go-buffruneio => github.com/pelletier/go-buffruneio v0.2.0
github.com/pelletier/go-toml => github.com/pelletier/go-toml v1.2.0
@@ -307,6 +305,7 @@ replace (
github.com/projectcalico/go-json => github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba
github.com/projectcalico/go-yaml => github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef
github.com/projectcalico/go-yaml-wrapper => github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee
github.com/projectcalico/kube-controllers => github.com/projectcalico/kube-controllers v3.8.8+incompatible
github.com/projectcalico/libcalico-go => github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce
github.com/prometheus/client_golang => github.com/prometheus/client_golang v0.9.4
github.com/prometheus/client_model => github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90

4
go.sum
View File

@@ -323,6 +323,8 @@ github.com/openshift/api v0.0.0-20180801171038-322a19404e37/go.mod h1:dh9o4Fs58g
github.com/openshift/generic-admission-server v1.14.0/go.mod h1:GD9KN/W4KxqRQGVMbqQHpHzb2XcQVvLCaBaSciqXvfM=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
@@ -344,6 +346,8 @@ github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef h1:Di9BaA9ap
github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef/go.mod h1:1Ra2BftSa7Go38Gbq1q0bfmBFSSgUv+Cdc3SY8IL/C0=
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee h1:yVWsNSlAuYoJ0CznHsYRPiFgsotoj07k00k5rQvGlHM=
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee/go.mod h1:UgC0aTQ2KMDxlX3lU/stndk7DMUBJqzN40yFiILHgxc=
github.com/projectcalico/kube-controllers v3.8.8+incompatible h1:ZbCg0wJ+gd7i81CB6vOASiUN//oR4ZBl+wEdy0Vk1uI=
github.com/projectcalico/kube-controllers v3.8.8+incompatible/go.mod h1:ZEafKeKN5wiNARRw1LZP8l10uEfp04C7redU848MMZw=
github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce h1:O/R67iwUe8TvZwgKbDB2cvF2/8L8PR4zVOcBtYEHD5Y=
github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce/go.mod h1:z4tuFqrAg/423AMSaDamY5LgqeOZ5ETui6iOxDwJ/ag=
github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A=

View File

@@ -1,11 +0,0 @@
approvers:
- magicsong
- zheng1
reviewers:
- magicsong
- zheng1
labels:
- area/deploy
- area/networking

View File

@@ -1,23 +0,0 @@
bases:
- ../crds
resources:
- network.yaml
- rbac/role.yaml
- rbac/role_binding.yaml
generatorOptions:
disableNameSuffixHash: true
secretGenerator:
- name: calico-etcd-secrets
files:
- etcd-ca=etcd/ca
- etcd-key=etcd/key
- etcd-cert=etcd/crt
type: Opaque
patchesStrategicMerge:
- patch_image_name.yaml
namespace: network-test-f22e8ea9

View File

@@ -1,57 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: network-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: network-manager
namespace: network-system
labels:
control-plane: network-manager
spec:
selector:
matchLabels:
control-plane: network-manager
replicas: 1
template:
metadata:
labels:
control-plane: network-manager
spec:
nodeSelector:
node-role.kubernetes.io/master: ""
hostNetwork: true
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/master"
effect: NoSchedule
containers:
- command:
- /ks-network
args:
- -v=4
- np-provider=calico
image: network:latest
imagePullPolicy: Always
name: manager
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /calicocerts
name: etcd-certs
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: etcd-certs
secret:
secretName: calico-etcd-secrets
defaultMode: 0400

View File

@@ -1,12 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: network-manager
namespace: network-system
spec:
template:
spec:
containers:
# Change the value of image field below to your controller image URL
- image: magicsong/ks-network:f22e8ea9
name: manager

View File

@@ -1,8 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
subjects:
- kind: ServiceAccount
name: default
namespace: network-test-f22e8ea9

View File

@@ -1,33 +0,0 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: network-manager
rules:
- apiGroups:
- network.kubesphere.io
resources:
- namespacenetworkpolicies
- workspacenetworkpolicies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- tenant.kubesphere.io
resources:
- workspaces
verbs:
- create
- delete
- get
- list
- patch
- update
- watch

View File

@@ -1,12 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: net-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: network-system

View File

@@ -1,11 +0,0 @@
bases:
- ../crds
resources:
- network.yaml
- role.yaml
patchesStrategicMerge:
- patch_image_name.yaml
namespace: network-test-f22e8ea9

View File

@@ -1,69 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: network-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: network-manager
namespace: network-system
labels:
control-plane: network-manager
spec:
selector:
matchLabels:
control-plane: network-manager
replicas: 1
template:
metadata:
labels:
control-plane: network-manager
spec:
nodeSelector:
node-role.kubernetes.io/master: ""
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/master"
effect: NoSchedule
serviceAccountName: network-manager
containers:
- command:
- /ks-network
args:
- -v=4
- np-provider=calico
- datastore-type=k8s
image: network:latest
imagePullPolicy: Always
name: manager
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
terminationGracePeriodSeconds: 10
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: net-role-binding
namespace: network-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: network-manager
subjects:
- kind: ServiceAccount
name: network-manager
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: network-manager

View File

@@ -1,12 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: network-manager
namespace: network-system
spec:
template:
spec:
containers:
# Change the value of image field below to your controller image URL
- image: magicsong/ks-network:f22e8ea9
name: manager

View File

@@ -1,8 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: net-role-binding
subjects:
- kind: ServiceAccount
name: network-manager
namespace: network-test-f22e8ea9

View File

@@ -1,54 +0,0 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: network-manager
rules:
- apiGroups:
- crd.projectcalico.org
resources:
- clusterinformations
- felixconfigurations
- globalfelixconfigs
- globalnetworkpolicies
- globalnetworksets
- hostendpoints
- ipamblocks
- ippools
- networkpolicies
- networksets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- network.kubesphere.io
resources:
- namespacenetworkpolicies
- workspacenetworkpolicies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- tenant.kubesphere.io
resources:
- workspaces
verbs:
- create
- delete
- get
- list
- patch
- update
- watch

View File

@@ -1,3 +0,0 @@
resources:
- wsnp.yaml
- nsnp.yaml

View File

@@ -1,711 +0,0 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: namespacenetworkpolicies.network.kubesphere.io
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: NamespaceNetworkPolicy
plural: namespacenetworkpolicies
shortNames:
- nsnp
scope: Namespaced
validation:
openAPIV3Schema:
description: NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies
API
properties:
apiVersion:
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: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
kind:
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: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy
properties:
egress:
description: The ordered set of egress rules. Each rule contains a
set of packet match criteria and a corresponding action to apply.
items:
description: "A Rule encapsulates a set of match criteria and an action.
\ Both selector-based security Policy and security Profiles reference
rules - separated out as a list of rules for both ingress and egress
packet matching. \n Each positive match criteria has a negated version,
prefixed with ”Not”. All the match criteria within a rule must be
satisfied for a packet to match. A single rule can contain the positive
and negative version of a match and both must be satisfied for the
rule to match."
properties:
action:
type: string
destination:
description: Destination contains the match criteria that apply
to destination entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
items:
type: object
x-kubernetes-int-or-string: true
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
http:
description: HTTP contains match criteria that apply to HTTP requests.
properties:
methods:
description: Methods is an optional field that restricts the
rule to apply only to HTTP requests that use one of the
listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods
are OR'd together.
items:
type: string
type: array
paths:
description: 'Paths is an optional field that restricts the
rule to apply to HTTP requests that use one of the listed
HTTP Paths. Multiple paths are OR''d together. e.g: - exact:
/foo - prefix: /bar NOTE: Each entry may ONLY specify either
a `exact` or a `prefix` match. The validator will check
for it.'
items:
description: 'HTTPPath specifies an HTTP path to match.
It may be either of the form: exact: <path>: which matches
the path exactly or prefix: <path-prefix>: which matches
the path prefix'
properties:
exact:
type: string
prefix:
type: string
type: object
type: array
type: object
icmp:
description: ICMP is an optional field that restricts the rule
to apply to a specific type and code of ICMP traffic. This
should only be specified if the Protocol field is set to "ICMP"
or "ICMPv6".
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
ipVersion:
description: IPVersion is an optional field that restricts the
rule to only match a specific IP version.
type: integer
notICMP:
description: NotICMP is the negated version of the ICMP field.
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
notProtocol:
description: NotProtocol is the negated version of the Protocol
field.
type: string
protocol:
description: "Protocol is an optional field that restricts the
rule to only apply to traffic of a specific IP protocol. Required
if any of the EntityRules contain Ports (because ports only
apply to certain protocols). \n Must be one of these string
values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\"
or an integer in the range 1-255."
type: string
source:
description: Source contains the match criteria that apply to
source entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
required:
- action
type: object
type: array
ingress:
description: The ordered set of ingress rules. Each rule contains a
set of packet match criteria and a corresponding action to apply.
items:
description: "A Rule encapsulates a set of match criteria and an action.
\ Both selector-based security Policy and security Profiles reference
rules - separated out as a list of rules for both ingress and egress
packet matching. \n Each positive match criteria has a negated version,
prefixed with ”Not”. All the match criteria within a rule must be
satisfied for a packet to match. A single rule can contain the positive
and negative version of a match and both must be satisfied for the
rule to match."
properties:
action:
type: string
destination:
description: Destination contains the match criteria that apply
to destination entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
anyOf:
- type: integer
- type: string
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
anyOf:
- type: integer
- type: string
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
http:
description: HTTP contains match criteria that apply to HTTP requests.
properties:
methods:
description: Methods is an optional field that restricts the
rule to apply only to HTTP requests that use one of the
listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods
are OR'd together.
items:
type: string
type: array
paths:
description: 'Paths is an optional field that restricts the
rule to apply to HTTP requests that use one of the listed
HTTP Paths. Multiple paths are OR''d together. e.g: - exact:
/foo - prefix: /bar NOTE: Each entry may ONLY specify either
a `exact` or a `prefix` match. The validator will check
for it.'
items:
description: 'HTTPPath specifies an HTTP path to match.
It may be either of the form: exact: <path>: which matches
the path exactly or prefix: <path-prefix>: which matches
the path prefix'
properties:
exact:
type: string
prefix:
type: string
type: object
type: array
type: object
icmp:
description: ICMP is an optional field that restricts the rule
to apply to a specific type and code of ICMP traffic. This
should only be specified if the Protocol field is set to "ICMP"
or "ICMPv6".
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
ipVersion:
description: IPVersion is an optional field that restricts the
rule to only match a specific IP version.
type: integer
notICMP:
description: NotICMP is the negated version of the ICMP field.
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
notProtocol:
description: NotProtocol is the negated version of the Protocol
field.
type: string
protocol:
description: "Protocol is an optional field that restricts the
rule to only apply to traffic of a specific IP protocol. Required
if any of the EntityRules contain Ports (because ports only
apply to certain protocols). \n Must be one of these string
values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\"
or an integer in the range 1-255."
type: string
source:
description: Source contains the match criteria that apply to
source entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
anyOf:
- type: integer
- type: string
type: object
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
required:
- action
type: object
type: array
order:
description: Order is an optional field that specifies the order in
which the policy is applied. Policies with higher "order" are applied
after those with lower order. If the order is omitted, it may be
considered to be "infinite" - i.e. the policy will be applied last. Policies
with identical order will be applied in alphanumerical order based
on the Policy "Name".
type: integer
selector:
description: "The selector is an expression used to pick pick out the
endpoints that the policy should be applied to. \n Selector expressions
follow this syntax: \n \tlabel == \"string_literal\" -> comparison,
e.g. my_label == \"foo bar\" \tlabel != \"string_literal\" -> not
equal; also matches if label is not present \tlabel in { \"a\", \"b\",
\"c\", ... } -> true if the value of label X is one of \"a\", \"b\",
\"c\" \tlabel not in { \"a\", \"b\", \"c\", ... } -> true if the
value of label X is not one of \"a\", \"b\", \"c\" \thas(label_name)
\ -> True if that label is present \t! expr -> negation of expr \texpr
&& expr -> Short-circuit and \texpr || expr -> Short-circuit or
\t( expr ) -> parens for grouping \tall() or the empty selector ->
matches all endpoints. \n Label names are allowed to contain alphanumerics,
-, _ and /. String literals are more permissive but they do not support
escape characters. \n Examples (with made-up labels): \n \ttype ==
\"webserver\" && deployment == \"prod\" \ttype in {\"frontend\", \"backend\"}
\tdeployment != \"dev\" \t! has(label_name)"
type: string
types:
description: "Types indicates whether this policy applies to ingress,
or to egress, or to both. When not explicitly specified (and so the
value on creation is empty or nil), Calico defaults Types according
to what Ingress and Egress are present in the policy. The default
is: \n - [ PolicyTypeIngress ], if there are no Egress rules (including
the case where there are also no Ingress rules) \n - [ PolicyTypeEgress
], if there are Egress rules but no Ingress rules \n - [ PolicyTypeIngress,
PolicyTypeEgress ], if there are both Ingress and Egress rules. \n
When the policy is read back again, Types will always be one of these
values, never empty or nil."
items:
type: string
type: array
required:
- selector
type: object
type: object
version: v1alpha1
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -1,523 +0,0 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: workspacenetworkpolicies.network.kubesphere.io
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: WorkspaceNetworkPolicy
plural: workspacenetworkpolicies
shortNames:
- wsnp
scope: Cluster
validation:
openAPIV3Schema:
description: WorkspaceNetworkPolicy is a set of network policies applied to
the scope to workspace
properties:
apiVersion:
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: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
kind:
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: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy
properties:
egress:
description: List of egress rules to be applied to the selected pods.
Outgoing traffic is allowed if there are no NetworkPolicies selecting
the pod (and cluster policy otherwise allows the traffic), OR if the
traffic matches at least one egress rule across all of the NetworkPolicy
objects whose podSelector matches the pod. If this field is empty
then this NetworkPolicy limits all outgoing traffic (and serves solely
to ensure that the pods it selects are isolated by default). This
field is beta-level in 1.8
items:
description: WorkspaceNetworkPolicyEgressRule describes a particular
set of traffic that is allowed out of pods matched by a WorkspaceNetworkPolicySpec's
podSelector. The traffic must match both ports and to.
properties:
from:
description: List of sources which should be able to access the
pods selected for this rule. Items in this list are combined
using a logical OR operation. If this field is empty or missing,
this rule matches all sources (traffic not restricted by source).
If this field is present and contains at least on item, this
rule allows traffic only if the traffic matches at least one
item in the from list.
items:
description: WorkspaceNetworkPolicyPeer describes a peer to
allow traffic from. Only certain combinations of fields are
allowed. It is same as 'NetworkPolicyPeer' in k8s but with
an additional field 'WorkspaceSelector'
properties:
ipBlock:
description: IPBlock defines policy on a particular IPBlock.
If this field is set then neither of the other fields
can be.
properties:
cidr:
description: CIDR is a string representing the IP Block
Valid examples are "192.168.1.1/24"
type: string
except:
description: Except is a slice of CIDRs that should
not be included within an IP Block Valid examples
are "192.168.1.1/24" Except values will be rejected
if they are outside the CIDR range
items:
type: string
type: array
required:
- cidr
type: object
namespaceSelector:
description: "Selects Namespaces using cluster-scoped labels.
This field follows standard label selector semantics;
if present but empty, it selects all namespaces. \n If
PodSelector is also set, then the NetworkPolicyPeer as
a whole selects the Pods matching PodSelector in the Namespaces
selected by NamespaceSelector. Otherwise it selects all
Pods in the Namespaces selected by NamespaceSelector."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
podSelector:
description: "This is a label selector which selects Pods.
This field follows standard label selector semantics;
if present but empty, it selects all pods. \n If NamespaceSelector
is also set, then the NetworkPolicyPeer as a whole selects
the Pods matching PodSelector in the Namespaces selected
by NamespaceSelector. Otherwise it selects the Pods matching
PodSelector in the policy's own Namespace."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
workspaceSelector:
description: A label selector is a label query over a set
of resources. The result of matchLabels and matchExpressions
are ANDed. An empty label selector matches all objects.
A null label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
type: object
type: array
ports:
description: List of ports which should be made accessible on
the pods selected for this rule. Each item in this list is combined
using a logical OR. If this field is empty or missing, this
rule matches all ports (traffic not restricted by port). If
this field is present and contains at least one item, then this
rule allows traffic only if the traffic matches at least one
port in the list.
items:
description: NetworkPolicyPort describes a port to allow traffic
on
properties:
port:
anyOf:
- type: string
- type: integer
description: The port on the given protocol. This can either
be a numerical or named port on a pod. If this field is
not provided, this matches all port names and numbers.
protocol:
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to TCP.
type: string
type: object
type: array
type: object
type: array
ingress:
description: List of ingress rules to be applied to the selected pods.
Traffic is allowed to a pod if there are no NetworkPolicies selecting
the pod (and cluster policy otherwise allows the traffic), OR if the
traffic source is the pod's local node, OR if the traffic matches
at least one ingress rule across all of the NetworkPolicy objects
whose podSelector matches the pod. If this field is empty then this
NetworkPolicy does not allow any traffic (and serves solely to ensure
that the pods it selects are isolated by default)
items:
description: WorkspaceNetworkPolicyIngressRule describes a particular
set of traffic that is allowed to the pods matched by a WorkspaceNetworkPolicySpec's
podSelector. The traffic must match both ports and from.
properties:
from:
description: List of sources which should be able to access the
pods selected for this rule. Items in this list are combined
using a logical OR operation. If this field is empty or missing,
this rule matches all sources (traffic not restricted by source).
If this field is present and contains at least on item, this
rule allows traffic only if the traffic matches at least one
item in the from list.
items:
description: WorkspaceNetworkPolicyPeer describes a peer to
allow traffic from. Only certain combinations of fields are
allowed. It is same as 'NetworkPolicyPeer' in k8s but with
an additional field 'WorkspaceSelector'
properties:
ipBlock:
description: IPBlock defines policy on a particular IPBlock.
If this field is set then neither of the other fields
can be.
properties:
cidr:
description: CIDR is a string representing the IP Block
Valid examples are "192.168.1.1/24"
type: string
except:
description: Except is a slice of CIDRs that should
not be included within an IP Block Valid examples
are "192.168.1.1/24" Except values will be rejected
if they are outside the CIDR range
items:
type: string
type: array
required:
- cidr
type: object
namespaceSelector:
description: "Selects Namespaces using cluster-scoped labels.
This field follows standard label selector semantics;
if present but empty, it selects all namespaces. \n If
PodSelector is also set, then the NetworkPolicyPeer as
a whole selects the Pods matching PodSelector in the Namespaces
selected by NamespaceSelector. Otherwise it selects all
Pods in the Namespaces selected by NamespaceSelector."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
podSelector:
description: "This is a label selector which selects Pods.
This field follows standard label selector semantics;
if present but empty, it selects all pods. \n If NamespaceSelector
is also set, then the NetworkPolicyPeer as a whole selects
the Pods matching PodSelector in the Namespaces selected
by NamespaceSelector. Otherwise it selects the Pods matching
PodSelector in the policy's own Namespace."
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
workspaceSelector:
description: A label selector is a label query over a set
of resources. The result of matchLabels and matchExpressions
are ANDed. An empty label selector matches all objects.
A null label selector matches no objects.
properties:
matchExpressions:
description: matchExpressions is a list of label selector
requirements. The requirements are ANDed.
items:
description: A label selector requirement is a selector
that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the selector
applies to.
type: string
operator:
description: operator represents a key's relationship
to a set of values. Valid operators are In,
NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values.
If the operator is In or NotIn, the values array
must be non-empty. If the operator is Exists
or DoesNotExist, the values array must be empty.
This array is replaced during a strategic merge
patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs.
A single {key,value} in the matchLabels map is equivalent
to an element of matchExpressions, whose key field
is "key", the operator is "In", and the values array
contains only "value". The requirements are ANDed.
type: object
type: object
type: object
type: array
ports:
description: List of ports which should be made accessible on
the pods selected for this rule. Each item in this list is combined
using a logical OR. If this field is empty or missing, this
rule matches all ports (traffic not restricted by port). If
this field is present and contains at least one item, then this
rule allows traffic only if the traffic matches at least one
port in the list.
items:
description: NetworkPolicyPort describes a port to allow traffic
on
properties:
port:
anyOf:
- type: string
- type: integer
description: The port on the given protocol. This can either
be a numerical or named port on a pod. If this field is
not provided, this matches all port names and numbers.
protocol:
description: The protocol (TCP, UDP, or SCTP) which traffic
must match. If not specified, this field defaults to TCP.
type: string
type: object
type: array
type: object
type: array
policyTypes:
description: List of rule types that the WorkspaceNetworkPolicy relates
to. Valid options are Ingress, Egress, or Ingress,Egress. If this
field is not specified, it will default based on the existence of
Ingress or Egress rules; policies that contain an Egress section are
assumed to affect Egress, and all policies (whether or not they contain
an Ingress section) are assumed to affect Ingress. If you want to
write an egress-only policy, you must explicitly specify policyTypes
[ "Egress" ]. Likewise, if you want to write a policy that specifies
that no egress is allowed, you must specify a policyTypes value that
include "Egress" (since such a policy would not include an Egress
section and would otherwise default to just [ "Ingress" ]).
items:
description: Policy Type string describes the NetworkPolicy type This
type is beta-level in 1.8
type: string
type: array
workspace:
description: Workspace specify the name of ws to apply this workspace
network policy
type: string
type: object
status:
description: WorkspaceNetworkPolicyStatus defines the observed state of
WorkspaceNetworkPolicy
type: object
type: object
version: v1alpha1
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -1,30 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: net-manager-role
rules:
- apiGroups:
- network.kubesphere.io
resources:
- namespacenetworkpolicies
- workspacenetworkpolicies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- tenant.kubesphere.io
resources:
- workspaces
verbs:
- create
- delete
- get
- list
- patch
- update
- watch

View File

@@ -1,170 +0,0 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
)
// A Rule encapsulates a set of match criteria and an action. Both selector-based security Policy
// and security Profiles reference rules - separated out as a list of rules for both
// ingress and egress packet matching.
//
// Each positive match criteria has a negated version, prefixed with ”Not”. All the match
// criteria within a rule must be satisfied for a packet to match. A single rule can contain
// the positive and negative version of a match and both must be satisfied for the rule to match.
type Rule struct {
Action Action `json:"action" validate:"action"`
// IPVersion is an optional field that restricts the rule to only match a specific IP
// version.
IPVersion *int `json:"ipVersion,omitempty" validate:"omitempty,ipVersion"`
// Protocol is an optional field that restricts the rule to only apply to traffic of
// a specific IP protocol. Required if any of the EntityRules contain Ports
// (because ports only apply to certain protocols).
//
// Must be one of these string values: "TCP", "UDP", "ICMP", "ICMPv6", "SCTP", "UDPLite"
// or an integer in the range 1-255.
Protocol *corev1.Protocol `json:"protocol,omitempty" validate:"omitempty"`
// ICMP is an optional field that restricts the rule to apply to a specific type and
// code of ICMP traffic. This should only be specified if the Protocol field is set to
// "ICMP" or "ICMPv6".
ICMP *ICMPFields `json:"icmp,omitempty" validate:"omitempty"`
// NotProtocol is the negated version of the Protocol field.
NotProtocol *corev1.Protocol `json:"notProtocol,omitempty" validate:"omitempty"`
// NotICMP is the negated version of the ICMP field.
NotICMP *ICMPFields `json:"notICMP,omitempty" validate:"omitempty"`
// Source contains the match criteria that apply to source entity.
Source EntityRule `json:"source,omitempty" validate:"omitempty"`
// Destination contains the match criteria that apply to destination entity.
Destination EntityRule `json:"destination,omitempty" validate:"omitempty"`
// HTTP contains match criteria that apply to HTTP requests.
HTTP *HTTPMatch `json:"http,omitempty" validate:"omitempty"`
}
// HTTPPath specifies an HTTP path to match. It may be either of the form:
// exact: <path>: which matches the path exactly or
// prefix: <path-prefix>: which matches the path prefix
type HTTPPath struct {
Exact string `json:"exact,omitempty" validate:"omitempty"`
Prefix string `json:"prefix,omitempty" validate:"omitempty"`
}
// HTTPMatch is an optional field that apply only to HTTP requests
// The Methods and Path fields are joined with AND
type HTTPMatch struct {
// Methods is an optional field that restricts the rule to apply only to HTTP requests that use one of the listed
// HTTP Methods (e.g. GET, PUT, etc.)
// Multiple methods are OR'd together.
Methods []string `json:"methods,omitempty" validate:"omitempty"`
// Paths is an optional field that restricts the rule to apply to HTTP requests that use one of the listed
// HTTP Paths.
// Multiple paths are OR'd together.
// e.g:
// - exact: /foo
// - prefix: /bar
// NOTE: Each entry may ONLY specify either a `exact` or a `prefix` match. The validator will check for it.
Paths []HTTPPath `json:"paths,omitempty" validate:"omitempty"`
}
// ICMPFields defines structure for ICMP and NotICMP sub-struct for ICMP code and type
type ICMPFields struct {
// Match on a specific ICMP type. For example a value of 8 refers to ICMP Echo Request
// (i.e. pings).
Type *int `json:"type,omitempty" validate:"omitempty,gte=0,lte=254"`
// Match on a specific ICMP code. If specified, the Type value must also be specified.
// This is a technical limitation imposed by the kernels iptables firewall, which
// Calico uses to enforce the rule.
Code *int `json:"code,omitempty" validate:"omitempty,gte=0,lte=255"`
}
// An EntityRule is a sub-component of a Rule comprising the match criteria specific
// to a particular entity (that is either the source or destination).
//
// A source EntityRule matches the source endpoint and originating traffic.
// A destination EntityRule matches the destination endpoint and terminating traffic.
type EntityRule struct {
// Nets is an optional field that restricts the rule to only apply to traffic that
// originates from (or terminates at) IP addresses in any of the given subnets.
Nets []string `json:"nets,omitempty" validate:"omitempty,dive,net"`
// Selector is an optional field that contains a selector expression (see Policy for
// sample syntax). Only traffic that originates from (terminates at) endpoints matching
// the selector will be matched.
//
// Note that: in addition to the negated version of the Selector (see NotSelector below), the
// selector expression syntax itself supports negation. The two types of negation are subtly
// different. One negates the set of matched endpoints, the other negates the whole match:
//
// Selector = "!has(my_label)" matches packets that are from other Calico-controlled
// endpoints that do not have the label “my_label”.
//
// NotSelector = "has(my_label)" matches packets that are not from Calico-controlled
// endpoints that do have the label “my_label”.
//
// The effect is that the latter will accept packets from non-Calico sources whereas the
// former is limited to packets from Calico-controlled endpoints.
Selector string `json:"selector,omitempty" validate:"omitempty,selector"`
// NamespaceSelector is an optional field that contains a selector expression. Only traffic
// that originates from (or terminates at) endpoints within the selected namespaces will be
// matched. When both NamespaceSelector and Selector are defined on the same rule, then only
// workload endpoints that are matched by both selectors will be selected by the rule.
//
// For NetworkPolicy, an empty NamespaceSelector implies that the Selector is limited to selecting
// only workload endpoints in the same namespace as the NetworkPolicy.
//
// For GlobalNetworkPolicy, an empty NamespaceSelector implies the Selector applies to workload
// endpoints across all namespaces.
NamespaceSelector string `json:"namespaceSelector,omitempty" validate:"omitempty,selector"`
// Ports is an optional field that restricts the rule to only apply to traffic that has a
// source (destination) port that matches one of these ranges/values. This value is a
// list of integers or strings that represent ranges of ports.
//
// Since only some protocols have ports, if any ports are specified it requires the
// Protocol match in the Rule to be set to "TCP" or "UDP".
Ports []numorstring.Port `json:"ports,omitempty" validate:"omitempty,dive"`
// NotNets is the negated version of the Nets field.
NotNets []string `json:"notNets,omitempty" validate:"omitempty,dive,net"`
// NotSelector is the negated version of the Selector field. See Selector field for
// subtleties with negated selectors.
NotSelector string `json:"notSelector,omitempty" validate:"omitempty,selector"`
// NotPorts is the negated version of the Ports field.
// Since only some protocols have ports, if any ports are specified it requires the
// Protocol match in the Rule to be set to "TCP" or "UDP".
NotPorts []numorstring.Port `json:"notPorts,omitempty" validate:"omitempty,dive"`
// ServiceAccounts is an optional field that restricts the rule to only apply to traffic that originates from (or
// terminates at) a pod running as a matching service account.
ServiceAccounts *ServiceAccountMatch `json:"serviceAccounts,omitempty" validate:"omitempty"`
}
type ServiceAccountMatch struct {
// Names is an optional field that restricts the rule to only apply to traffic that originates from (or terminates
// at) a pod running as a service account whose name is in the list.
Names []string `json:"names,omitempty" validate:"omitempty"`
// Selector is an optional field that restricts the rule to only apply to traffic that originates from
// (or terminates at) a pod running as a service account that matches the given label selector.
// If both Names and Selector are specified then they are AND'ed.
Selector string `json:"selector,omitempty" validate:"omitempty,selector"`
}
type Action string
const (
Allow Action = "Allow"
Deny = "Deny"
Log = "Log"
Pass = "Pass"
)
type PolicyType string
const (
PolicyTypeIngress PolicyType = "Ingress"
PolicyTypeEgress PolicyType = "Egress"
)

View File

@@ -17,68 +17,114 @@ limitations under the License.
package v1alpha1
import (
k8snet "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// All types in this file is copy from calicoapi as we use calico to policy
const (
ResourceKindNamespaceNetworkPolicy = "NamespaceNetworkPolicy"
ResourceSingularNamespaceNetworkPolicy = "namespacenetworkpolicy"
ResourcePluralNamespaceNetworkPolicy = "namespacenetworkpolicies"
)
// NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy
// NamespaceNetworkPolicySpec provides the specification of a NamespaceNetworkPolicy
type NamespaceNetworkPolicySpec struct {
// Order is an optional field that specifies the order in which the policy is applied.
// Policies with higher "order" are applied after those with lower
// order. If the order is omitted, it may be considered to be "infinite" - i.e. the
// policy will be applied last. Policies with identical order will be applied in
// alphanumerical order based on the Policy "Name".
Order *int `json:"order,omitempty"`
// The ordered set of ingress rules. Each rule contains a set of packet match criteria and
// a corresponding action to apply.
Ingress []Rule `json:"ingress,omitempty" validate:"omitempty,dive"`
// The ordered set of egress rules. Each rule contains a set of packet match criteria and
// a corresponding action to apply.
Egress []Rule `json:"egress,omitempty" validate:"omitempty,dive"`
// The selector is an expression used to pick pick out the endpoints that the policy should
// be applied to.
//
// Selector expressions follow this syntax:
//
// label == "string_literal" -> comparison, e.g. my_label == "foo bar"
// label != "string_literal" -> not equal; also matches if label is not present
// label in { "a", "b", "c", ... } -> true if the value of label X is one of "a", "b", "c"
// label not in { "a", "b", "c", ... } -> true if the value of label X is not one of "a", "b", "c"
// has(label_name) -> True if that label is present
// ! expr -> negation of expr
// expr && expr -> Short-circuit and
// expr || expr -> Short-circuit or
// ( expr ) -> parens for grouping
// all() or the empty selector -> matches all endpoints.
//
// Label names are allowed to contain alphanumerics, -, _ and /. String literals are more permissive
// but they do not support escape characters.
//
// Examples (with made-up labels):
//
// type == "webserver" && deployment == "prod"
// type in {"frontend", "backend"}
// deployment != "dev"
// ! has(label_name)
Selector string `json:"selector" validate:"selector"`
// Types indicates whether this policy applies to ingress, or to egress, or to both. When
// not explicitly specified (and so the value on creation is empty or nil), Calico defaults
// Types according to what Ingress and Egress are present in the policy. The
// default is:
//
// - [ PolicyTypeIngress ], if there are no Egress rules (including the case where there are
// also no Ingress rules)
//
// - [ PolicyTypeEgress ], if there are Egress rules but no Ingress rules
//
// - [ PolicyTypeIngress, PolicyTypeEgress ], if there are both Ingress and Egress rules.
//
// When the policy is read back again, Types will always be one of these values, never empty
// or nil.
Types []PolicyType `json:"types,omitempty" validate:"omitempty,dive,policyType"`
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// List of ingress rules to be applied to the selected pods. Traffic is allowed to
// a pod if there are no NetworkPolicies selecting the pod
// (and cluster policy otherwise allows the traffic), OR if the traffic source is
// the pod's local node, OR if the traffic matches at least one ingress rule
// across all of the NetworkPolicy objects whose podSelector matches the pod. If
// this field is empty then this NetworkPolicy does not allow any traffic (and serves
// solely to ensure that the pods it selects are isolated by default)
// +optional
Ingress []NetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,1,rep,name=ingress"`
// List of egress rules to be applied to the selected pods. Outgoing traffic is
// allowed if there are no NetworkPolicies selecting the pod (and cluster policy
// otherwise allows the traffic), OR if the traffic matches at least one egress rule
// across all of the NetworkPolicy objects whose podSelector matches the pod. If
// this field is empty then this NetworkPolicy limits all outgoing traffic (and serves
// solely to ensure that the pods it selects are isolated by default).
// This field is beta-level in 1.8
// +optional
Egress []NetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,2,rep,name=egress"`
// List of rule types that the NetworkPolicy relates to.
// Valid options are "Ingress", "Egress", or "Ingress,Egress".
// If this field is not specified, it will default based on the existence of Ingress or Egress rules;
// policies that contain an Egress section are assumed to affect Egress, and all policies
// (whether or not they contain an Ingress section) are assumed to affect Ingress.
// If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ].
// Likewise, if you want to write a policy that specifies that no egress is allowed,
// you must specify a policyTypes value that include "Egress" (since such a policy would not include
// an Egress section and would otherwise default to just [ "Ingress" ]).
// This field is beta-level in 1.8
// +optional
PolicyTypes []k8snet.PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,3,rep,name=policyTypes,casttype=PolicyType"`
}
// NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods
// matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.
type NetworkPolicyIngressRule struct {
// List of ports which should be made accessible on the pods selected for this
// rule. Each item in this list is combined using a logical OR. If this field is
// empty or missing, this rule matches all ports (traffic not restricted by port).
// If this field is present and contains at least one item, then this rule allows
// traffic only if the traffic matches at least one port in the list.
// +optional
Ports []k8snet.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"`
// List of sources which should be able to access the pods selected for this rule.
// Items in this list are combined using a logical OR operation. If this field is
// empty or missing, this rule matches all sources (traffic not restricted by
// source). If this field is present and contains at least one item, this rule
// allows traffic only if the traffic matches at least one item in the from list.
// +optional
From []NetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"`
}
// NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods
// matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to.
// This type is beta-level in 1.8
type NetworkPolicyEgressRule struct {
// List of destination ports for outgoing traffic.
// Each item in this list is combined using a logical OR. If this field is
// empty or missing, this rule matches all ports (traffic not restricted by port).
// If this field is present and contains at least one item, then this rule allows
// traffic only if the traffic matches at least one port in the list.
// +optional
Ports []k8snet.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"`
// List of destinations for outgoing traffic of pods selected for this rule.
// Items in this list are combined using a logical OR operation. If this field is
// empty or missing, this rule matches all destinations (traffic not restricted by
// destination). If this field is present and contains at least one item, this rule
// allows traffic only if the traffic matches at least one item in the to list.
// +optional
To []NetworkPolicyPeer `json:"to,omitempty" protobuf:"bytes,2,rep,name=to"`
}
type NamespaceSelector struct {
Name string `json:"name" protobuf:"bytes,1,name=name"`
}
type ServiceSelector struct {
Name string `json:"name" protobuf:"bytes,1,name=name"`
Namespace string `json:"namespace" protobuf:"bytes,2,name=namespace"`
}
// NetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of
// fields are allowed
type NetworkPolicyPeer struct {
// +optional
NamespaceSelector *NamespaceSelector `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"`
// IPBlock defines policy on a particular IPBlock. If this field is set then
// neither of the other fields can be.
// +optional
IPBlock *k8snet.IPBlock `json:"ipBlock,omitempty" protobuf:"bytes,2,rep,name=ipBlock"`
ServiceSelector *ServiceSelector `json:"service,omitempty" protobuf:"bytes,3,opt,name=service"`
}
// +genclient

View File

@@ -1,73 +0,0 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)
type ASNumber uint32
// ASNumberFromString creates an ASNumber struct from a string value. The
// string value may simply be a number or may be the ASN in dotted notation.
func ASNumberFromString(s string) (ASNumber, error) {
if num, err := strconv.ParseUint(s, 10, 32); err == nil {
return ASNumber(num), nil
}
parts := strings.Split(s, ".")
if len(parts) != 2 {
msg := fmt.Sprintf("invalid AS Number format (%s)", s)
return 0, errors.New(msg)
}
if num1, err := strconv.ParseUint(parts[0], 10, 16); err != nil {
msg := fmt.Sprintf("invalid AS Number format (%s)", s)
return 0, errors.New(msg)
} else if num2, err := strconv.ParseUint(parts[1], 10, 16); err != nil {
msg := fmt.Sprintf("invalid AS Number format (%s)", s)
return 0, errors.New(msg)
} else {
return ASNumber((num1 << 16) + num2), nil
}
}
// UnmarshalJSON implements the json.Unmarshaller uinterface.
func (a *ASNumber) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, (*uint32)(a)); err == nil {
return nil
} else {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if v, err := ASNumberFromString(s); err != nil {
return err
} else {
*a = v
return nil
}
}
}
// String returns the string value, or the Itoa of the uint value.
func (a ASNumber) String() string {
return strconv.FormatUint(uint64(a), 10)
}

View File

@@ -1,19 +0,0 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package numorstring implements a set of type definitions that in YAML or JSON
format may be represented by either a number or a string.
*/
package numorstring

View File

@@ -1,26 +0,0 @@
// Copyright (c) 2016,2018 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestNumorstring(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Numorstring Suite")
}

View File

@@ -1,204 +0,0 @@
// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring_test
import (
"encoding/json"
"fmt"
"reflect"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"github.com/projectcalico/libcalico-go/lib/numorstring"
)
func init() {
asNumberType := reflect.TypeOf(numorstring.ASNumber(0))
protocolType := reflect.TypeOf(numorstring.Protocol{})
portType := reflect.TypeOf(numorstring.Port{})
// Perform tests of JSON unmarshaling of the various field types.
DescribeTable("NumOrStringJSONUnmarshaling",
func(jtext string, typ reflect.Type, expected interface{}) {
// Create a new field type and invoke the unmarshaller interface
// directly (this covers a couple more error cases than calling
// through json.Unmarshal.
new := reflect.New(typ)
u := new.Interface().(json.Unmarshaler)
err := u.UnmarshalJSON([]byte(jtext))
if expected != nil {
Expect(err).To(BeNil(),
"expected json unmarshal to not error")
Expect(new.Elem().Interface()).To(Equal(expected),
"expected value not same as json unmarshalled value")
} else {
Expect(err).ToNot(BeNil(),
"expected json unmarshal to error")
}
},
// ASNumber tests.
Entry("should accept 0 AS number as int", "0", asNumberType, numorstring.ASNumber(0)),
Entry("should accept 4294967295 AS number as int", "4294967295", asNumberType, numorstring.ASNumber(4294967295)),
Entry("should accept 0 AS number as string", "\"0\"", asNumberType, numorstring.ASNumber(0)),
Entry("should accept 4294967295 AS number as string", "\"4294967295\"", asNumberType, numorstring.ASNumber(4294967295)),
Entry("should accept 1.10 AS number as string", "\"1.10\"", asNumberType, numorstring.ASNumber(65546)),
Entry("should accept 00.00 AS number as string", "\"00.00\"", asNumberType, numorstring.ASNumber(0)),
Entry("should accept 00.01 AS number as string", "\"00.01\"", asNumberType, numorstring.ASNumber(1)),
Entry("should accept 65535.65535 AS number as string", "\"65535.65535\"", asNumberType, numorstring.ASNumber(4294967295)),
Entry("should reject 1.1.1 AS number as string", "\"1.1.1\"", asNumberType, nil),
Entry("should reject 65536.65535 AS number as string", "\"65536.65535\"", asNumberType, nil),
Entry("should reject 65535.65536 AS number as string", "\"65535.65536\"", asNumberType, nil),
Entry("should reject 0.-1 AS number as string", "\"0.-1\"", asNumberType, nil),
Entry("should reject -1 AS number as int", "-1", asNumberType, nil),
Entry("should reject 4294967296 AS number as int", "4294967296", asNumberType, nil),
// Port tests.
Entry("should accept 0 port as int", "0", portType, numorstring.SinglePort(0)),
Entry("should accept 65535 port as int", "65535", portType, numorstring.SinglePort(65535)),
Entry("should accept 0:65535 port range as string", "\"0:65535\"", portType, portFromRange(0, 65535)),
Entry("should accept 1:10 port range as string", "\"1:10\"", portType, portFromRange(1, 10)),
Entry("should accept foo-bar as named port", "\"foo-bar\"", portType, numorstring.NamedPort("foo-bar")),
Entry("should reject -1 port as int", "-1", portType, nil),
Entry("should reject 65536 port as int", "65536", portType, nil),
Entry("should reject 0:65536 port range as string", "\"0:65536\"", portType, nil),
Entry("should reject -1:65535 port range as string", "\"-1:65535\"", portType, nil),
Entry("should reject 10:1 port range as string", "\"10:1\"", portType, nil),
Entry("should reject 1:2:3 port range as string", "\"1:2:3\"", portType, nil),
Entry("should reject bad named port string", "\"*\"", portType, nil),
Entry("should reject bad port string", "\"1:2", portType, nil),
// Protocol tests. Invalid integer values will be stored as strings.
Entry("should accept 0 protocol as int", "0", protocolType, numorstring.ProtocolFromInt(0)),
Entry("should accept 255 protocol as int", "255", protocolType, numorstring.ProtocolFromInt(255)),
Entry("should accept tcp protocol as string", "\"TCP\"", protocolType, numorstring.ProtocolFromString("TCP")),
Entry("should accept tcp protocol as string", "\"TCP\"", protocolType, numorstring.ProtocolFromString("TCP")),
Entry("should accept 0 protocol as string", "\"0\"", protocolType, numorstring.ProtocolFromInt(0)),
Entry("should accept 0 protocol as string", "\"255\"", protocolType, numorstring.ProtocolFromInt(255)),
Entry("should accept 256 protocol as string", "\"256\"", protocolType, numorstring.ProtocolFromString("256")),
Entry("should reject bad protocol string", "\"25", protocolType, nil),
)
// Perform tests of JSON marshaling of the various field types.
DescribeTable("NumOrStringJSONMarshaling",
func(field interface{}, jtext string) {
b, err := json.Marshal(field)
if jtext != "" {
Expect(err).To(BeNil(),
"expected json marshal to not error")
Expect(string(b)).To(Equal(jtext),
"expected json not same as marshalled value")
} else {
Expect(err).ToNot(BeNil(),
"expected json marshal to error")
}
},
// ASNumber tests.
Entry("should marshal ASN of 0", numorstring.ASNumber(0), "0"),
Entry("should marshal ASN of 4294967295", numorstring.ASNumber(4294967295), "4294967295"),
// Port tests.
Entry("should marshal port of 0", numorstring.SinglePort(0), "0"),
Entry("should marshal port of 65535", portFromRange(65535, 65535), "65535"),
Entry("should marshal port of 10", portFromString("10"), "10"),
Entry("should marshal port range of 10:20", portFromRange(10, 20), "\"10:20\""),
Entry("should marshal port range of 20:30", portFromRange(20, 30), "\"20:30\""),
Entry("should marshal named port", numorstring.NamedPort("foobar"), `"foobar"`),
// Protocol tests.
Entry("should marshal protocol of 0", numorstring.ProtocolFromInt(0), "0"),
Entry("should marshal protocol of udp", numorstring.ProtocolFromString("UDP"), "\"UDP\""),
)
// Perform tests of Stringer interface various field types.
DescribeTable("NumOrStringStringify",
func(field interface{}, s string) {
a := fmt.Sprint(field)
Expect(a).To(Equal(s),
"expected String() value to match")
},
// ASNumber tests.
Entry("should stringify ASN of 0", numorstring.ASNumber(0), "0"),
Entry("should stringify ASN of 4294967295", numorstring.ASNumber(4294967295), "4294967295"),
// Port tests.
Entry("should stringify port of 20", numorstring.SinglePort(20), "20"),
Entry("should stringify port range of 10:20", portFromRange(10, 20), "10:20"),
// Protocol tests.
Entry("should stringify protocol of 0", numorstring.ProtocolFromInt(0), "0"),
Entry("should stringify protocol of udp", numorstring.ProtocolFromString("UDP"), "UDP"),
)
// Perform tests of Protocols supporting ports.
DescribeTable("NumOrStringProtocolsSupportingPorts",
func(protocol numorstring.Protocol, supportsPorts bool) {
Expect(protocol.SupportsPorts()).To(Equal(supportsPorts),
"expected protocol port support to match")
},
Entry("protocol 6 supports ports", numorstring.ProtocolFromInt(6), true),
Entry("protocol 17 supports ports", numorstring.ProtocolFromInt(17), true),
Entry("protocol udp supports ports", numorstring.ProtocolFromString("UDP"), true),
Entry("protocol udp supports ports", numorstring.ProtocolFromString("TCP"), true),
Entry("protocol foo does not support ports", numorstring.ProtocolFromString("foo"), false),
Entry("protocol 2 does not support ports", numorstring.ProtocolFromInt(2), false),
)
// Perform tests of Protocols FromString method.
DescribeTable("NumOrStringProtocols FromString is not case sensitive",
func(input, expected string) {
Expect(numorstring.ProtocolFromString(input).StrVal).To(Equal(expected),
"expected parsed protocol to match")
},
Entry("protocol udp -> UDP", "udp", "UDP"),
Entry("protocol tcp -> TCP", "tcp", "TCP"),
Entry("protocol updlite -> UDPLite", "udplite", "UDPLite"),
Entry("unknown protocol xxxXXX", "xxxXXX", "xxxXXX"),
)
// Perform tests of Protocols FromStringV1 method.
DescribeTable("NumOrStringProtocols FromStringV1 is lowercase",
func(input, expected string) {
Expect(numorstring.ProtocolFromStringV1(input).StrVal).To(Equal(expected),
"expected parsed protocol to match")
},
Entry("protocol udp -> UDP", "UDP", "udp"),
Entry("protocol tcp -> TCP", "TCP", "tcp"),
Entry("protocol updlite -> UDPLite", "UDPLite", "udplite"),
Entry("unknown protocol xxxXXX", "xxxXXX", "xxxxxx"),
)
// Perform tests of Protocols ToV1 method.
DescribeTable("NumOrStringProtocols FromStringV1 is lowercase",
func(input, expected numorstring.Protocol) {
Expect(input.ToV1()).To(Equal(expected),
"expected parsed protocol to match")
},
// Protocol tests.
Entry("protocol udp -> UDP", numorstring.ProtocolFromInt(2), numorstring.ProtocolFromInt(2)),
Entry("protocol tcp -> TCP", numorstring.ProtocolFromString("TCP"), numorstring.ProtocolFromStringV1("TCP")),
)
}
func portFromRange(minPort, maxPort uint16) numorstring.Port {
p, _ := numorstring.PortFromRange(minPort, maxPort)
return p
}
func portFromString(s string) numorstring.Port {
p, _ := numorstring.PortFromString(s)
return p
}

View File

@@ -1,144 +0,0 @@
// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strconv"
)
// Port represents either a range of numeric ports or a named port.
//
// - For a named port, set the PortName, leaving MinPort and MaxPort as 0.
// - For a port range, set MinPort and MaxPort to the (inclusive) port numbers. Set
// PortName to "".
// - For a single port, set MinPort = MaxPort and PortName = "".
type Port struct {
MinPort uint16 `json:"minPort,omitempty"`
MaxPort uint16 `json:"maxPort,omitempty"`
PortName string `validate:"omitempty,portName" json:"portName,omitempty"`
}
// SinglePort creates a Port struct representing a single port.
func SinglePort(port uint16) Port {
return Port{MinPort: port, MaxPort: port}
}
func NamedPort(name string) Port {
return Port{PortName: name}
}
// PortFromRange creates a Port struct representing a range of ports.
func PortFromRange(minPort, maxPort uint16) (Port, error) {
port := Port{MinPort: minPort, MaxPort: maxPort}
if minPort > maxPort {
msg := fmt.Sprintf("minimum port number (%d) is greater than maximum port number (%d) in port range", minPort, maxPort)
return port, errors.New(msg)
}
return port, nil
}
var (
allDigits = regexp.MustCompile(`^\d+$`)
portRange = regexp.MustCompile(`^(\d+):(\d+)$`)
nameRegex = regexp.MustCompile("^[a-zA-Z0-9_.-]{1,128}$")
)
// PortFromString creates a Port struct from its string representation. A port
// may either be single value "1234", a range of values "100:200" or a named port: "name".
func PortFromString(s string) (Port, error) {
if allDigits.MatchString(s) {
// Port is all digits, it should parse as a single port.
num, err := strconv.ParseUint(s, 10, 16)
if err != nil {
msg := fmt.Sprintf("invalid port format (%s)", s)
return Port{}, errors.New(msg)
}
return SinglePort(uint16(num)), nil
}
if groups := portRange.FindStringSubmatch(s); len(groups) > 0 {
// Port matches <digits>:<digits>, it should parse as a range of ports.
if pmin, err := strconv.ParseUint(groups[1], 10, 16); err != nil {
msg := fmt.Sprintf("invalid minimum port number in range (%s)", s)
return Port{}, errors.New(msg)
} else if pmax, err := strconv.ParseUint(groups[2], 10, 16); err != nil {
msg := fmt.Sprintf("invalid maximum port number in range (%s)", s)
return Port{}, errors.New(msg)
} else {
return PortFromRange(uint16(pmin), uint16(pmax))
}
}
if !nameRegex.MatchString(s) {
msg := fmt.Sprintf("invalid name for named port (%s)", s)
return Port{}, errors.New(msg)
}
return NamedPort(s), nil
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (p *Port) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if v, err := PortFromString(s); err != nil {
return err
} else {
*p = v
return nil
}
}
// It's not a string, it must be a single int.
var i uint16
if err := json.Unmarshal(b, &i); err != nil {
return err
}
v := SinglePort(i)
*p = v
return nil
}
// MarshalJSON implements the json.Marshaller interface.
func (p Port) MarshalJSON() ([]byte, error) {
if p.PortName != "" {
return json.Marshal(p.PortName)
} else if p.MinPort == p.MaxPort {
return json.Marshal(p.MinPort)
} else {
return json.Marshal(p.String())
}
}
// String returns the string value. If the min and max port are the same
// this returns a single string representation of the port number, otherwise
// if returns a colon separated range of ports.
func (p Port) String() string {
if p.PortName != "" {
return p.PortName
} else if p.MinPort == p.MaxPort {
return strconv.FormatUint(uint64(p.MinPort), 10)
} else {
return fmt.Sprintf("%d:%d", p.MinPort, p.MaxPort)
}
}

View File

@@ -1,134 +0,0 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring
import "strings"
const (
ProtocolUDP = "UDP"
ProtocolTCP = "TCP"
ProtocolICMP = "ICMP"
ProtocolICMPv6 = "ICMPv6"
ProtocolSCTP = "SCTP"
ProtocolUDPLite = "UDPLite"
ProtocolUDPV1 = "udp"
ProtocolTCPV1 = "tcp"
)
var (
allProtocolNames = []string{
ProtocolUDP,
ProtocolTCP,
ProtocolICMP,
ProtocolICMPv6,
ProtocolSCTP,
ProtocolUDPLite,
}
)
type Protocol Uint8OrString
// ProtocolFromInt creates a Protocol struct from an integer value.
func ProtocolFromInt(p uint8) Protocol {
return Protocol(
Uint8OrString{Type: NumOrStringNum, NumVal: p},
)
}
// ProtocolV3FromProtocolV1 creates a v3 Protocol from a v1 Protocol,
// while handling case conversion.
func ProtocolV3FromProtocolV1(p Protocol) Protocol {
if p.Type == NumOrStringNum {
return p
}
for _, n := range allProtocolNames {
if strings.ToLower(n) == strings.ToLower(p.StrVal) {
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: n},
)
}
}
return p
}
// ProtocolFromString creates a Protocol struct from a string value.
func ProtocolFromString(p string) Protocol {
for _, n := range allProtocolNames {
if strings.ToLower(n) == strings.ToLower(p) {
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: n},
)
}
}
// Unknown protocol - return the value unchanged. Validation should catch this.
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: p},
)
}
// ProtocolFromStringV1 creates a Protocol struct from a string value (for the v1 API)
func ProtocolFromStringV1(p string) Protocol {
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: strings.ToLower(p)},
)
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (p *Protocol) UnmarshalJSON(b []byte) error {
return (*Uint8OrString)(p).UnmarshalJSON(b)
}
// MarshalJSON implements the json.Marshaller interface.
func (p Protocol) MarshalJSON() ([]byte, error) {
return Uint8OrString(p).MarshalJSON()
}
// String returns the string value, or the Itoa of the int value.
func (p Protocol) String() string {
return (Uint8OrString)(p).String()
}
// String returns the string value, or the Itoa of the int value.
func (p Protocol) ToV1() Protocol {
if p.Type == NumOrStringNum {
return p
}
return ProtocolFromStringV1(p.StrVal)
}
// NumValue returns the NumVal if type Int, or if
// it is a String, will attempt a conversion to int.
func (p Protocol) NumValue() (uint8, error) {
return (Uint8OrString)(p).NumValue()
}
// SupportsProtocols returns whether this protocol supports ports. This returns true if
// the numerical or string verion of the protocol indicates TCP (6) or UDP (17).
func (p Protocol) SupportsPorts() bool {
num, err := p.NumValue()
if err == nil {
return num == 6 || num == 17
} else {
switch p.StrVal {
case ProtocolTCP, ProtocolUDP, ProtocolTCPV1, ProtocolUDPV1:
return true
}
return false
}
}

View File

@@ -1,23 +0,0 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring
// Type represents the stored type of Int32OrString.
type NumOrStringType int
const (
NumOrStringNum NumOrStringType = iota // The structure holds a number.
NumOrStringString // The structure holds a string.
)

View File

@@ -1,80 +0,0 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package numorstring
import (
"encoding/json"
"strconv"
)
// UInt8OrString is a type that can hold an uint8 or a string. When used in
// JSON or YAML marshalling and unmarshalling, it produces or consumes the
// inner type. This allows you to have, for example, a JSON field that can
// accept a name or number.
type Uint8OrString struct {
Type NumOrStringType
NumVal uint8
StrVal string
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (i *Uint8OrString) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
num, err := strconv.ParseUint(s, 10, 8)
if err == nil {
i.Type = NumOrStringNum
i.NumVal = uint8(num)
} else {
i.Type = NumOrStringString
i.StrVal = s
}
return nil
}
i.Type = NumOrStringNum
return json.Unmarshal(b, &i.NumVal)
}
// MarshalJSON implements the json.Marshaller interface.
func (i Uint8OrString) MarshalJSON() ([]byte, error) {
if num, err := i.NumValue(); err == nil {
return json.Marshal(num)
} else {
return json.Marshal(i.StrVal)
}
}
// String returns the string value, or the Itoa of the int value.
func (i Uint8OrString) String() string {
if i.Type == NumOrStringString {
return i.StrVal
}
return strconv.FormatUint(uint64(i.NumVal), 10)
}
// NumValue returns the NumVal if type Int, or if
// it is a String, will attempt a conversion to int.
func (i Uint8OrString) NumValue() (uint8, error) {
if i.Type == NumOrStringString {
num, err := strconv.ParseUint(i.StrVal, 10, 8)
return uint8(num), err
}
return i.NumVal, nil
}

View File

@@ -31,78 +31,70 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"k8s.io/api/networking/v1.IPBlock": schema_k8sio_api_networking_v1_IPBlock(ref),
"k8s.io/api/networking/v1.NetworkPolicy": schema_k8sio_api_networking_v1_NetworkPolicy(ref),
"k8s.io/api/networking/v1.NetworkPolicyEgressRule": schema_k8sio_api_networking_v1_NetworkPolicyEgressRule(ref),
"k8s.io/api/networking/v1.NetworkPolicyIngressRule": schema_k8sio_api_networking_v1_NetworkPolicyIngressRule(ref),
"k8s.io/api/networking/v1.NetworkPolicyList": schema_k8sio_api_networking_v1_NetworkPolicyList(ref),
"k8s.io/api/networking/v1.NetworkPolicyPeer": schema_k8sio_api_networking_v1_NetworkPolicyPeer(ref),
"k8s.io/api/networking/v1.NetworkPolicyPort": schema_k8sio_api_networking_v1_NetworkPolicyPort(ref),
"k8s.io/api/networking/v1.NetworkPolicySpec": schema_k8sio_api_networking_v1_NetworkPolicySpec(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref),
"k8s.io/apimachinery/pkg/util/intstr.IntOrString": schema_apimachinery_pkg_util_intstr_IntOrString(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule": schema_pkg_apis_network_v1alpha1_EntityRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPMatch": schema_pkg_apis_network_v1alpha1_HTTPMatch(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPPath": schema_pkg_apis_network_v1alpha1_HTTPPath(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields": schema_pkg_apis_network_v1alpha1_ICMPFields(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicyList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule": schema_pkg_apis_network_v1alpha1_Rule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceAccountMatch": schema_pkg_apis_network_v1alpha1_ServiceAccountMatch(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicy(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyEgressRule": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyEgressRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyIngressRule": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyIngressRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyPeer(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyStatus": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyStatus(ref),
"k8s.io/api/networking/v1.IPBlock": schema_k8sio_api_networking_v1_IPBlock(ref),
"k8s.io/api/networking/v1.NetworkPolicy": schema_k8sio_api_networking_v1_NetworkPolicy(ref),
"k8s.io/api/networking/v1.NetworkPolicyEgressRule": schema_k8sio_api_networking_v1_NetworkPolicyEgressRule(ref),
"k8s.io/api/networking/v1.NetworkPolicyIngressRule": schema_k8sio_api_networking_v1_NetworkPolicyIngressRule(ref),
"k8s.io/api/networking/v1.NetworkPolicyList": schema_k8sio_api_networking_v1_NetworkPolicyList(ref),
"k8s.io/api/networking/v1.NetworkPolicyPeer": schema_k8sio_api_networking_v1_NetworkPolicyPeer(ref),
"k8s.io/api/networking/v1.NetworkPolicyPort": schema_k8sio_api_networking_v1_NetworkPolicyPort(ref),
"k8s.io/api/networking/v1.NetworkPolicySpec": schema_k8sio_api_networking_v1_NetworkPolicySpec(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref),
"k8s.io/apimachinery/pkg/util/intstr.IntOrString": schema_apimachinery_pkg_util_intstr_IntOrString(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicyList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceSelector": schema_pkg_apis_network_v1alpha1_NamespaceSelector(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule": schema_pkg_apis_network_v1alpha1_NetworkPolicyEgressRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule": schema_pkg_apis_network_v1alpha1_NetworkPolicyIngressRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer": schema_pkg_apis_network_v1alpha1_NetworkPolicyPeer(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector": schema_pkg_apis_network_v1alpha1_ServiceSelector(ref),
}
}
@@ -2523,196 +2515,6 @@ func schema_apimachinery_pkg_util_intstr_IntOrString(ref common.ReferenceCallbac
}
}
func schema_pkg_apis_network_v1alpha1_EntityRule(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "An EntityRule is a sub-component of a Rule comprising the match criteria specific to a particular entity (that is either the source or destination).\n\nA source EntityRule matches the source endpoint and originating traffic. A destination EntityRule matches the destination endpoint and terminating traffic.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"nets": {
SchemaProps: spec.SchemaProps{
Description: "Nets is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) IP addresses in any of the given subnets.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"selector": {
SchemaProps: spec.SchemaProps{
Description: "Selector is an optional field that contains a selector expression (see Policy for sample syntax). Only traffic that originates from (terminates at) endpoints matching the selector will be matched.\n\nNote that: in addition to the negated version of the Selector (see NotSelector below), the selector expression syntax itself supports negation. The two types of negation are subtly different. One negates the set of matched endpoints, the other negates the whole match:\n\n\tSelector = \"!has(my_label)\" matches packets that are from other Calico-controlled\n\tendpoints that do not have the label “my_label”.\n\n\tNotSelector = \"has(my_label)\" matches packets that are not from Calico-controlled\n\tendpoints that do have the label “my_label”.\n\nThe effect is that the latter will accept packets from non-Calico sources whereas the former is limited to packets from Calico-controlled endpoints.",
Type: []string{"string"},
Format: "",
},
},
"namespaceSelector": {
SchemaProps: spec.SchemaProps{
Description: "NamespaceSelector is an optional field that contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector and Selector are defined on the same rule, then only workload endpoints that are matched by both selectors will be selected by the rule.\n\nFor NetworkPolicy, an empty NamespaceSelector implies that the Selector is limited to selecting only workload endpoints in the same namespace as the NetworkPolicy.\n\nFor GlobalNetworkPolicy, an empty NamespaceSelector implies the Selector applies to workload endpoints across all namespaces.",
Type: []string{"string"},
Format: "",
},
},
"ports": {
SchemaProps: spec.SchemaProps{
Description: "Ports is an optional field that restricts the rule to only apply to traffic that has a source (destination) port that matches one of these ranges/values. This value is a list of integers or strings that represent ranges of ports.\n\nSince only some protocols have ports, if any ports are specified it requires the Protocol match in the Rule to be set to \"TCP\" or \"UDP\".",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring.Port"),
},
},
},
},
},
"notNets": {
SchemaProps: spec.SchemaProps{
Description: "NotNets is the negated version of the Nets field.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"notSelector": {
SchemaProps: spec.SchemaProps{
Description: "NotSelector is the negated version of the Selector field. See Selector field for subtleties with negated selectors.",
Type: []string{"string"},
Format: "",
},
},
"notPorts": {
SchemaProps: spec.SchemaProps{
Description: "NotPorts is the negated version of the Ports field. Since only some protocols have ports, if any ports are specified it requires the Protocol match in the Rule to be set to \"TCP\" or \"UDP\".",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring.Port"),
},
},
},
},
},
"serviceAccounts": {
SchemaProps: spec.SchemaProps{
Description: "ServiceAccounts is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) a pod running as a matching service account.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceAccountMatch"),
},
},
},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceAccountMatch", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring.Port"},
}
}
func schema_pkg_apis_network_v1alpha1_HTTPMatch(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "HTTPMatch is an optional field that apply only to HTTP requests The Methods and Path fields are joined with AND",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"methods": {
SchemaProps: spec.SchemaProps{
Description: "Methods is an optional field that restricts the rule to apply only to HTTP requests that use one of the listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods are OR'd together.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"paths": {
SchemaProps: spec.SchemaProps{
Description: "Paths is an optional field that restricts the rule to apply to HTTP requests that use one of the listed HTTP Paths. Multiple paths are OR'd together. e.g: - exact: /foo - prefix: /bar NOTE: Each entry may ONLY specify either a `exact` or a `prefix` match. The validator will check for it.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPPath"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPPath"},
}
}
func schema_pkg_apis_network_v1alpha1_HTTPPath(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "HTTPPath specifies an HTTP path to match. It may be either of the form: exact: <path>: which matches the path exactly or prefix: <path-prefix>: which matches the path prefix",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"exact": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"prefix": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_ICMPFields(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ICMPFields defines structure for ICMP and NotICMP sub-struct for ICMP code and type",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Match on a specific ICMP type. For example a value of 8 refers to ICMP Echo Request (i.e. pings).",
Type: []string{"integer"},
Format: "int32",
},
},
"code": {
SchemaProps: spec.SchemaProps{
Description: "Match on a specific ICMP code. If specified, the Type value must also be specified. This is a technical limitation imposed by the kernels iptables firewall, which Calico uses to enforce the rule.",
Type: []string{"integer"},
Format: "int32",
},
},
},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -2803,417 +2605,9 @@ func schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref common.Refe
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy",
Description: "NetworkPolicySpec provides the specification of a NetworkPolicy",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"order": {
SchemaProps: spec.SchemaProps{
Description: "Order is an optional field that specifies the order in which the policy is applied. Policies with higher \"order\" are applied after those with lower order. If the order is omitted, it may be considered to be \"infinite\" - i.e. the policy will be applied last. Policies with identical order will be applied in alphanumerical order based on the Policy \"Name\".",
Type: []string{"integer"},
Format: "int32",
},
},
"ingress": {
SchemaProps: spec.SchemaProps{
Description: "The ordered set of ingress rules. Each rule contains a set of packet match criteria and a corresponding action to apply.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule"),
},
},
},
},
},
"egress": {
SchemaProps: spec.SchemaProps{
Description: "The ordered set of egress rules. Each rule contains a set of packet match criteria and a corresponding action to apply.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule"),
},
},
},
},
},
"selector": {
SchemaProps: spec.SchemaProps{
Description: "The selector is an expression used to pick pick out the endpoints that the policy should be applied to.\n\nSelector expressions follow this syntax:\n\n\tlabel == \"string_literal\" -> comparison, e.g. my_label == \"foo bar\"\n\tlabel != \"string_literal\" -> not equal; also matches if label is not present\n\tlabel in { \"a\", \"b\", \"c\", ... } -> true if the value of label X is one of \"a\", \"b\", \"c\"\n\tlabel not in { \"a\", \"b\", \"c\", ... } -> true if the value of label X is not one of \"a\", \"b\", \"c\"\n\thas(label_name) -> True if that label is present\n\t! expr -> negation of expr\n\texpr && expr -> Short-circuit and\n\texpr || expr -> Short-circuit or\n\t( expr ) -> parens for grouping\n\tall() or the empty selector -> matches all endpoints.\n\nLabel names are allowed to contain alphanumerics, -, _ and /. String literals are more permissive but they do not support escape characters.\n\nExamples (with made-up labels):\n\n\ttype == \"webserver\" && deployment == \"prod\"\n\ttype in {\"frontend\", \"backend\"}\n\tdeployment != \"dev\"\n\t! has(label_name)",
Type: []string{"string"},
Format: "",
},
},
"types": {
SchemaProps: spec.SchemaProps{
Description: "Types indicates whether this policy applies to ingress, or to egress, or to both. When not explicitly specified (and so the value on creation is empty or nil), Calico defaults Types according to what Ingress and Egress are present in the policy. The default is:\n\n- [ PolicyTypeIngress ], if there are no Egress rules (including the case where there are\n also no Ingress rules)\n\n- [ PolicyTypeEgress ], if there are Egress rules but no Ingress rules\n\n- [ PolicyTypeIngress, PolicyTypeEgress ], if there are both Ingress and Egress rules.\n\nWhen the policy is read back again, Types will always be one of these values, never empty or nil.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
Required: []string{"selector"},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule"},
}
}
func schema_pkg_apis_network_v1alpha1_Rule(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "A Rule encapsulates a set of match criteria and an action. Both selector-based security Policy and security Profiles reference rules - separated out as a list of rules for both ingress and egress packet matching.\n\nEach positive match criteria has a negated version, prefixed with ”Not”. All the match criteria within a rule must be satisfied for a packet to match. A single rule can contain the positive and negative version of a match and both must be satisfied for the rule to match.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"action": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"ipVersion": {
SchemaProps: spec.SchemaProps{
Description: "IPVersion is an optional field that restricts the rule to only match a specific IP version.",
Type: []string{"integer"},
Format: "int32",
},
},
"protocol": {
SchemaProps: spec.SchemaProps{
Description: "Protocol is an optional field that restricts the rule to only apply to traffic of a specific IP protocol. Required if any of the EntityRules contain Ports (because ports only apply to certain protocols).\n\nMust be one of these string values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\" or an integer in the range 1-255.",
Type: []string{"string"},
Format: "",
},
},
"icmp": {
SchemaProps: spec.SchemaProps{
Description: "ICMP is an optional field that restricts the rule to apply to a specific type and code of ICMP traffic. This should only be specified if the Protocol field is set to \"ICMP\" or \"ICMPv6\".",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields"),
},
},
"notProtocol": {
SchemaProps: spec.SchemaProps{
Description: "NotProtocol is the negated version of the Protocol field.",
Type: []string{"string"},
Format: "",
},
},
"notICMP": {
SchemaProps: spec.SchemaProps{
Description: "NotICMP is the negated version of the ICMP field.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields"),
},
},
"source": {
SchemaProps: spec.SchemaProps{
Description: "Source contains the match criteria that apply to source entity.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule"),
},
},
"destination": {
SchemaProps: spec.SchemaProps{
Description: "Destination contains the match criteria that apply to destination entity.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule"),
},
},
"http": {
SchemaProps: spec.SchemaProps{
Description: "HTTP contains match criteria that apply to HTTP requests.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPMatch"),
},
},
},
Required: []string{"action"},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPMatch", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields"},
}
}
func schema_pkg_apis_network_v1alpha1_ServiceAccountMatch(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"names": {
SchemaProps: spec.SchemaProps{
Description: "Names is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) a pod running as a service account whose name is in the list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"selector": {
SchemaProps: spec.SchemaProps{
Description: "Selector is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) a pod running as a service account that matches the given label selector. If both Names and Selector are specified then they are AND'ed.",
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicy is a set of network policies applied to the scope to workspace",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicySpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyStatus"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicySpec", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyStatus"},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyEgressRule(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and to.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"ports": {
SchemaProps: spec.SchemaProps{
Description: "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"),
},
},
},
},
},
"from": {
SchemaProps: spec.SchemaProps{
Description: "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyIngressRule(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and from.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"ports": {
SchemaProps: spec.SchemaProps{
Description: "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"),
},
},
},
},
},
"from": {
SchemaProps: spec.SchemaProps{
Description: "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicyList contains a list of WorkspaceNetworkPolicy",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
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: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicy"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicy"},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyPeer(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed. It is same as 'NetworkPolicyPeer' in k8s but with an additional field 'WorkspaceSelector'",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"podSelector": {
SchemaProps: spec.SchemaProps{
Description: "This is a label selector which selects Pods. This field follows standard label selector semantics; if present but empty, it selects all pods.\n\nIf NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects the Pods matching PodSelector in the policy's own Namespace.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
"namespaceSelector": {
SchemaProps: spec.SchemaProps{
Description: "Selects Namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
"ipBlock": {
SchemaProps: spec.SchemaProps{
Description: "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be.",
Ref: ref("k8s.io/api/networking/v1.IPBlock"),
},
},
"workspaceSelector": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1.IPBlock", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"workspace": {
SchemaProps: spec.SchemaProps{
Description: "Workspace specify the name of ws to apply this workspace network policy",
Type: []string{"string"},
Format: "",
},
},
"policyTypes": {
SchemaProps: spec.SchemaProps{
Description: "List of rule types that the WorkspaceNetworkPolicy relates to. Valid options are Ingress, Egress, or Ingress,Egress. If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]).",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"ingress": {
SchemaProps: spec.SchemaProps{
Description: "List of ingress rules to be applied to the selected pods. Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic source is the pod's local node, OR if the traffic matches at least one ingress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy does not allow any traffic (and serves solely to ensure that the pods it selects are isolated by default)",
@@ -3221,7 +2615,7 @@ func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.Refe
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyIngressRule"),
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule"),
},
},
},
@@ -3234,7 +2628,21 @@ func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.Refe
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyEgressRule"),
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule"),
},
},
},
},
},
"policyTypes": {
SchemaProps: spec.SchemaProps{
Description: "List of rule types that the NetworkPolicy relates to. Valid options are \"Ingress\", \"Egress\", or \"Ingress,Egress\". If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]). This field is beta-level in 1.8",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
@@ -3244,16 +2652,163 @@ func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.Refe
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyEgressRule", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyIngressRule"},
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule"},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_network_v1alpha1_NamespaceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "WorkspaceNetworkPolicyStatus defines the observed state of WorkspaceNetworkPolicy",
Type: []string{"object"},
Type: []string{"object"},
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"name"},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_NetworkPolicyEgressRule(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"ports": {
SchemaProps: spec.SchemaProps{
Description: "List of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"),
},
},
},
},
},
"to": {
SchemaProps: spec.SchemaProps{
Description: "List of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"},
}
}
func schema_pkg_apis_network_v1alpha1_NetworkPolicyIngressRule(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"ports": {
SchemaProps: spec.SchemaProps{
Description: "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"),
},
},
},
},
},
"from": {
SchemaProps: spec.SchemaProps{
Description: "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the from list.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"},
}
}
func schema_pkg_apis_network_v1alpha1_NetworkPolicyPeer(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "NetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"namespace": {
SchemaProps: spec.SchemaProps{
Description: "Selects Namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceSelector"),
},
},
"ipBlock": {
SchemaProps: spec.SchemaProps{
Description: "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be.",
Ref: ref("k8s.io/api/networking/v1.IPBlock"),
},
},
"service": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1.IPBlock", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceSelector", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector"},
}
}
func schema_pkg_apis_network_v1alpha1_ServiceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"namespace": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"name", "namespace"},
},
},
}

View File

@@ -33,7 +33,7 @@ var c client.Client
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "kustomize", "network", "crds")},
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
}
err := SchemeBuilder.AddToScheme(scheme.Scheme)

View File

@@ -1,145 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
k8snetworkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
ResourceKindWorkspaceNetworkPolicy = "WorkspaceNetworkPolicy"
ResourceSingularWorkspaceNetworkPolicy = "workspacenetworkpolicy"
ResourcePluralWorkspaceNetworkPolicy = "workspacenetworkpolicies"
)
// WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy
type WorkspaceNetworkPolicySpec struct {
// Workspace specify the name of ws to apply this workspace network policy
Workspace string `json:"workspace,omitempty"`
// List of rule types that the WorkspaceNetworkPolicy relates to.
// Valid options are Ingress, Egress, or Ingress,Egress.
// If this field is not specified, it will default based on the existence of Ingress or Egress rules;
// policies that contain an Egress section are assumed to affect Egress, and all policies
// (whether or not they contain an Ingress section) are assumed to affect Ingress.
// If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ].
// Likewise, if you want to write a policy that specifies that no egress is allowed,
// you must specify a policyTypes value that include "Egress" (since such a policy would not include
// an Egress section and would otherwise default to just [ "Ingress" ]).
// +optional
PolicyTypes []k8snetworkv1.PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,4,rep,name=policyTypes,casttype=PolicyType"`
// List of ingress rules to be applied to the selected pods. Traffic is allowed to
// a pod if there are no NetworkPolicies selecting the pod
// (and cluster policy otherwise allows the traffic), OR if the traffic source is
// the pod's local node, OR if the traffic matches at least one ingress rule
// across all of the NetworkPolicy objects whose podSelector matches the pod. If
// this field is empty then this NetworkPolicy does not allow any traffic (and serves
// solely to ensure that the pods it selects are isolated by default)
// +optional
Ingress []WorkspaceNetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,2,rep,name=ingress"`
// List of egress rules to be applied to the selected pods. Outgoing traffic is
// allowed if there are no NetworkPolicies selecting the pod (and cluster policy
// otherwise allows the traffic), OR if the traffic matches at least one egress rule
// across all of the NetworkPolicy objects whose podSelector matches the pod. If
// this field is empty then this NetworkPolicy limits all outgoing traffic (and serves
// solely to ensure that the pods it selects are isolated by default).
// This field is beta-level in 1.8
// +optional
Egress []WorkspaceNetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,3,rep,name=egress"`
}
// WorkspaceNetworkPolicyStatus defines the observed state of WorkspaceNetworkPolicy
type WorkspaceNetworkPolicyStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// WorkspaceNetworkPolicy is a set of network policies applied to the scope to workspace
// +k8s:openapi-gen=true
// +kubebuilder:resource:categories="networking",scope="Cluster",shortName="wsnp"
type WorkspaceNetworkPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec WorkspaceNetworkPolicySpec `json:"spec,omitempty"`
Status WorkspaceNetworkPolicyStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// WorkspaceNetworkPolicyList contains a list of WorkspaceNetworkPolicy
type WorkspaceNetworkPolicyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []WorkspaceNetworkPolicy `json:"items"`
}
// WorkspaceNetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods
// matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and from.
type WorkspaceNetworkPolicyIngressRule struct {
// List of ports which should be made accessible on the pods selected for this
// rule. Each item in this list is combined using a logical OR. If this field is
// empty or missing, this rule matches all ports (traffic not restricted by port).
// If this field is present and contains at least one item, then this rule allows
// traffic only if the traffic matches at least one port in the list.
// +optional
Ports []k8snetworkv1.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"`
// List of sources which should be able to access the pods selected for this rule.
// Items in this list are combined using a logical OR operation. If this field is
// empty or missing, this rule matches all sources (traffic not restricted by
// source). If this field is present and contains at least on item, this rule
// allows traffic only if the traffic matches at least one item in the from list.
// +optional
From []WorkspaceNetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"`
}
// WorkspaceNetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of
// fields are allowed. It is same as 'NetworkPolicyPeer' in k8s but with an additional field 'WorkspaceSelector'
type WorkspaceNetworkPolicyPeer struct {
k8snetworkv1.NetworkPolicyPeer `json:",inline"`
WorkspaceSelector *metav1.LabelSelector `json:"workspaceSelector,omitempty"`
}
// WorkspaceNetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods
// matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and to.
type WorkspaceNetworkPolicyEgressRule struct {
// List of ports which should be made accessible on the pods selected for this
// rule. Each item in this list is combined using a logical OR. If this field is
// empty or missing, this rule matches all ports (traffic not restricted by port).
// If this field is present and contains at least one item, then this rule allows
// traffic only if the traffic matches at least one port in the list.
// +optional
Ports []k8snetworkv1.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"`
// List of sources which should be able to access the pods selected for this rule.
// Items in this list are combined using a logical OR operation. If this field is
// empty or missing, this rule matches all sources (traffic not restricted by
// source). If this field is present and contains at least on item, this rule
// allows traffic only if the traffic matches at least one item in the from list.
// +optional
To []WorkspaceNetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"`
}
func init() {
SchemeBuilder.Register(&WorkspaceNetworkPolicy{}, &WorkspaceNetworkPolicyList{})
}

View File

@@ -1,56 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"testing"
"github.com/onsi/gomega"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
func TestStorageWorkspaceNetworkPolicy(t *testing.T) {
key := types.NamespacedName{
Name: "foo",
}
created := &WorkspaceNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
}}
g := gomega.NewGomegaWithT(t)
// Test Create
fetched := &WorkspaceNetworkPolicy{}
g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
g.Expect(fetched).To(gomega.Equal(created))
// Test Updating the Labels
updated := fetched.DeepCopy()
updated.Labels = map[string]string{"hello": "world"}
g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
g.Expect(fetched).To(gomega.Equal(updated))
// Test Delete
g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed())
}

View File

@@ -16,129 +16,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by controller-gen. DO NOT EDIT.
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/api/networking/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EntityRule) DeepCopyInto(out *EntityRule) {
*out = *in
if in.Nets != nil {
in, out := &in.Nets, &out.Nets
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]numorstring.Port, len(*in))
copy(*out, *in)
}
if in.NotNets != nil {
in, out := &in.NotNets, &out.NotNets
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.NotPorts != nil {
in, out := &in.NotPorts, &out.NotPorts
*out = make([]numorstring.Port, len(*in))
copy(*out, *in)
}
if in.ServiceAccounts != nil {
in, out := &in.ServiceAccounts, &out.ServiceAccounts
*out = new(ServiceAccountMatch)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EntityRule.
func (in *EntityRule) DeepCopy() *EntityRule {
if in == nil {
return nil
}
out := new(EntityRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPMatch) DeepCopyInto(out *HTTPMatch) {
*out = *in
if in.Methods != nil {
in, out := &in.Methods, &out.Methods
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Paths != nil {
in, out := &in.Paths, &out.Paths
*out = make([]HTTPPath, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatch.
func (in *HTTPMatch) DeepCopy() *HTTPMatch {
if in == nil {
return nil
}
out := new(HTTPMatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPPath) DeepCopyInto(out *HTTPPath) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPath.
func (in *HTTPPath) DeepCopy() *HTTPPath {
if in == nil {
return nil
}
out := new(HTTPPath)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ICMPFields) DeepCopyInto(out *ICMPFields) {
*out = *in
if in.Type != nil {
in, out := &in.Type, &out.Type
*out = new(int)
**out = **in
}
if in.Code != nil {
in, out := &in.Code, &out.Code
*out = new(int)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ICMPFields.
func (in *ICMPFields) DeepCopy() *ICMPFields {
if in == nil {
return nil
}
out := new(ICMPFields)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceNetworkPolicy) DeepCopyInto(out *NamespaceNetworkPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicy.
@@ -171,6 +64,7 @@ func (in *NamespaceNetworkPolicyList) DeepCopyInto(out *NamespaceNetworkPolicyLi
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicyList.
@@ -194,30 +88,26 @@ func (in *NamespaceNetworkPolicyList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceNetworkPolicySpec) DeepCopyInto(out *NamespaceNetworkPolicySpec) {
*out = *in
if in.Order != nil {
in, out := &in.Order, &out.Order
*out = new(int)
**out = **in
}
if in.Ingress != nil {
in, out := &in.Ingress, &out.Ingress
*out = make([]Rule, len(*in))
*out = make([]NetworkPolicyIngressRule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Egress != nil {
in, out := &in.Egress, &out.Egress
*out = make([]Rule, len(*in))
*out = make([]NetworkPolicyEgressRule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Types != nil {
in, out := &in.Types, &out.Types
*out = make([]PolicyType, len(*in))
if in.PolicyTypes != nil {
in, out := &in.PolicyTypes, &out.PolicyTypes
*out = make([]v1.PolicyType, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicySpec.
@@ -231,255 +121,124 @@ func (in *NamespaceNetworkPolicySpec) DeepCopy() *NamespaceNetworkPolicySpec {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) {
*out = *in
if in.IPVersion != nil {
in, out := &in.IPVersion, &out.IPVersion
*out = new(int)
**out = **in
}
if in.Protocol != nil {
in, out := &in.Protocol, &out.Protocol
*out = new(v1.Protocol)
**out = **in
}
if in.ICMP != nil {
in, out := &in.ICMP, &out.ICMP
*out = new(ICMPFields)
(*in).DeepCopyInto(*out)
}
if in.NotProtocol != nil {
in, out := &in.NotProtocol, &out.NotProtocol
*out = new(v1.Protocol)
**out = **in
}
if in.NotICMP != nil {
in, out := &in.NotICMP, &out.NotICMP
*out = new(ICMPFields)
(*in).DeepCopyInto(*out)
}
in.Source.DeepCopyInto(&out.Source)
in.Destination.DeepCopyInto(&out.Destination)
if in.HTTP != nil {
in, out := &in.HTTP, &out.HTTP
*out = new(HTTPMatch)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
func (in *Rule) DeepCopy() *Rule {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector.
func (in *NamespaceSelector) DeepCopy() *NamespaceSelector {
if in == nil {
return nil
}
out := new(Rule)
out := new(NamespaceSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceAccountMatch) DeepCopyInto(out *ServiceAccountMatch) {
*out = *in
if in.Names != nil {
in, out := &in.Names, &out.Names
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountMatch.
func (in *ServiceAccountMatch) DeepCopy() *ServiceAccountMatch {
if in == nil {
return nil
}
out := new(ServiceAccountMatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicy) DeepCopyInto(out *WorkspaceNetworkPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicy.
func (in *WorkspaceNetworkPolicy) DeepCopy() *WorkspaceNetworkPolicy {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WorkspaceNetworkPolicy) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPolicyEgressRule) {
func (in *NetworkPolicyEgressRule) DeepCopyInto(out *NetworkPolicyEgressRule) {
*out = *in
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]networkingv1.NetworkPolicyPort, len(*in))
*out = make([]v1.NetworkPolicyPort, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.To != nil {
in, out := &in.To, &out.To
*out = make([]WorkspaceNetworkPolicyPeer, len(*in))
*out = make([]NetworkPolicyPeer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyEgressRule.
func (in *WorkspaceNetworkPolicyEgressRule) DeepCopy() *WorkspaceNetworkPolicyEgressRule {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEgressRule.
func (in *NetworkPolicyEgressRule) DeepCopy() *NetworkPolicyEgressRule {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicyEgressRule)
out := new(NetworkPolicyEgressRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkPolicyIngressRule) {
func (in *NetworkPolicyIngressRule) DeepCopyInto(out *NetworkPolicyIngressRule) {
*out = *in
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]networkingv1.NetworkPolicyPort, len(*in))
*out = make([]v1.NetworkPolicyPort, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.From != nil {
in, out := &in.From, &out.From
*out = make([]WorkspaceNetworkPolicyPeer, len(*in))
*out = make([]NetworkPolicyPeer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyIngressRule.
func (in *WorkspaceNetworkPolicyIngressRule) DeepCopy() *WorkspaceNetworkPolicyIngressRule {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyIngressRule.
func (in *NetworkPolicyIngressRule) DeepCopy() *NetworkPolicyIngressRule {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicyIngressRule)
out := new(NetworkPolicyIngressRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyList) DeepCopyInto(out *WorkspaceNetworkPolicyList) {
func (in *NetworkPolicyPeer) DeepCopyInto(out *NetworkPolicyPeer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]WorkspaceNetworkPolicy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
if in.NamespaceSelector != nil {
in, out := &in.NamespaceSelector, &out.NamespaceSelector
*out = new(NamespaceSelector)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyList.
func (in *WorkspaceNetworkPolicyList) DeepCopy() *WorkspaceNetworkPolicyList {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WorkspaceNetworkPolicyList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyPeer) DeepCopyInto(out *WorkspaceNetworkPolicyPeer) {
*out = *in
in.NetworkPolicyPeer.DeepCopyInto(&out.NetworkPolicyPeer)
if in.WorkspaceSelector != nil {
in, out := &in.WorkspaceSelector, &out.WorkspaceSelector
*out = new(metav1.LabelSelector)
if in.IPBlock != nil {
in, out := &in.IPBlock, &out.IPBlock
*out = new(v1.IPBlock)
(*in).DeepCopyInto(*out)
}
if in.ServiceSelector != nil {
in, out := &in.ServiceSelector, &out.ServiceSelector
*out = new(ServiceSelector)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyPeer.
func (in *WorkspaceNetworkPolicyPeer) DeepCopy() *WorkspaceNetworkPolicyPeer {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyPeer.
func (in *NetworkPolicyPeer) DeepCopy() *NetworkPolicyPeer {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicyPeer)
out := new(NetworkPolicyPeer)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySpec) {
func (in *ServiceSelector) DeepCopyInto(out *ServiceSelector) {
*out = *in
if in.PolicyTypes != nil {
in, out := &in.PolicyTypes, &out.PolicyTypes
*out = make([]networkingv1.PolicyType, len(*in))
copy(*out, *in)
}
if in.Ingress != nil {
in, out := &in.Ingress, &out.Ingress
*out = make([]WorkspaceNetworkPolicyIngressRule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Egress != nil {
in, out := &in.Egress, &out.Egress
*out = make([]WorkspaceNetworkPolicyEgressRule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicySpec.
func (in *WorkspaceNetworkPolicySpec) DeepCopy() *WorkspaceNetworkPolicySpec {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSelector.
func (in *ServiceSelector) DeepCopy() *ServiceSelector {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyStatus) DeepCopyInto(out *WorkspaceNetworkPolicyStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyStatus.
func (in *WorkspaceNetworkPolicyStatus) DeepCopy() *WorkspaceNetworkPolicyStatus {
if in == nil {
return nil
}
out := new(WorkspaceNetworkPolicyStatus)
out := new(ServiceSelector)
in.DeepCopyInto(out)
return out
}

View File

@@ -2263,6 +2263,12 @@ func schema_pkg_apis_tenant_v1alpha1_WorkspaceSpec(ref common.ReferenceCallback)
Format: "",
},
},
"networkIsolation": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
},
},

View File

@@ -34,7 +34,8 @@ const (
// WorkspaceSpec defines the desired state of Workspace
type WorkspaceSpec struct {
Manager string `json:"manager,omitempty"`
Manager string `json:"manager,omitempty"`
NetworkIsolation bool `json:"networkIsolation,omitempty"`
}
// WorkspaceStatus defines the observed state of Workspace

View File

@@ -32,10 +32,6 @@ func (c *FakeNetworkV1alpha1) NamespaceNetworkPolicies(namespace string) v1alpha
return &FakeNamespaceNetworkPolicies{c, namespace}
}
func (c *FakeNetworkV1alpha1) WorkspaceNetworkPolicies() v1alpha1.WorkspaceNetworkPolicyInterface {
return &FakeWorkspaceNetworkPolicies{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeNetworkV1alpha1) RESTClient() rest.Interface {

View File

@@ -1,131 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
// FakeWorkspaceNetworkPolicies implements WorkspaceNetworkPolicyInterface
type FakeWorkspaceNetworkPolicies struct {
Fake *FakeNetworkV1alpha1
}
var workspacenetworkpoliciesResource = schema.GroupVersionResource{Group: "network.kubesphere.io", Version: "v1alpha1", Resource: "workspacenetworkpolicies"}
var workspacenetworkpoliciesKind = schema.GroupVersionKind{Group: "network.kubesphere.io", Version: "v1alpha1", Kind: "WorkspaceNetworkPolicy"}
// Get takes name of the workspaceNetworkPolicy, and returns the corresponding workspaceNetworkPolicy object, and an error if there is any.
func (c *FakeWorkspaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootGetAction(workspacenetworkpoliciesResource, name), &v1alpha1.WorkspaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.WorkspaceNetworkPolicy), err
}
// List takes label and field selectors, and returns the list of WorkspaceNetworkPolicies that match those selectors.
func (c *FakeWorkspaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.WorkspaceNetworkPolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootListAction(workspacenetworkpoliciesResource, workspacenetworkpoliciesKind, opts), &v1alpha1.WorkspaceNetworkPolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.WorkspaceNetworkPolicyList{ListMeta: obj.(*v1alpha1.WorkspaceNetworkPolicyList).ListMeta}
for _, item := range obj.(*v1alpha1.WorkspaceNetworkPolicyList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested workspaceNetworkPolicies.
func (c *FakeWorkspaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewRootWatchAction(workspacenetworkpoliciesResource, opts))
}
// Create takes the representation of a workspaceNetworkPolicy and creates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any.
func (c *FakeWorkspaceNetworkPolicies) Create(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(workspacenetworkpoliciesResource, workspaceNetworkPolicy), &v1alpha1.WorkspaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.WorkspaceNetworkPolicy), err
}
// Update takes the representation of a workspaceNetworkPolicy and updates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any.
func (c *FakeWorkspaceNetworkPolicies) Update(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateAction(workspacenetworkpoliciesResource, workspaceNetworkPolicy), &v1alpha1.WorkspaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.WorkspaceNetworkPolicy), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeWorkspaceNetworkPolicies) UpdateStatus(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(workspacenetworkpoliciesResource, "status", workspaceNetworkPolicy), &v1alpha1.WorkspaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.WorkspaceNetworkPolicy), err
}
// Delete takes name of the workspaceNetworkPolicy and deletes it. Returns an error if one occurs.
func (c *FakeWorkspaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewRootDeleteAction(workspacenetworkpoliciesResource, name), &v1alpha1.WorkspaceNetworkPolicy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeWorkspaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewRootDeleteCollectionAction(workspacenetworkpoliciesResource, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.WorkspaceNetworkPolicyList{})
return err
}
// Patch applies the patch and returns the patched workspaceNetworkPolicy.
func (c *FakeWorkspaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceAction(workspacenetworkpoliciesResource, name, pt, data, subresources...), &v1alpha1.WorkspaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.WorkspaceNetworkPolicy), err
}

View File

@@ -19,5 +19,3 @@ limitations under the License.
package v1alpha1
type NamespaceNetworkPolicyExpansion interface{}
type WorkspaceNetworkPolicyExpansion interface{}

View File

@@ -27,7 +27,6 @@ import (
type NetworkV1alpha1Interface interface {
RESTClient() rest.Interface
NamespaceNetworkPoliciesGetter
WorkspaceNetworkPoliciesGetter
}
// NetworkV1alpha1Client is used to interact with features provided by the network.kubesphere.io group.
@@ -39,10 +38,6 @@ func (c *NetworkV1alpha1Client) NamespaceNetworkPolicies(namespace string) Names
return newNamespaceNetworkPolicies(c, namespace)
}
func (c *NetworkV1alpha1Client) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInterface {
return newWorkspaceNetworkPolicies(c)
}
// NewForConfig creates a new NetworkV1alpha1Client for the given config.
func NewForConfig(c *rest.Config) (*NetworkV1alpha1Client, error) {
config := *c

View File

@@ -1,180 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
)
// WorkspaceNetworkPoliciesGetter has a method to return a WorkspaceNetworkPolicyInterface.
// A group's client should implement this interface.
type WorkspaceNetworkPoliciesGetter interface {
WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInterface
}
// WorkspaceNetworkPolicyInterface has methods to work with WorkspaceNetworkPolicy resources.
type WorkspaceNetworkPolicyInterface interface {
Create(*v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error)
Update(*v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error)
UpdateStatus(*v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.WorkspaceNetworkPolicy, error)
List(opts v1.ListOptions) (*v1alpha1.WorkspaceNetworkPolicyList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkspaceNetworkPolicy, err error)
WorkspaceNetworkPolicyExpansion
}
// workspaceNetworkPolicies implements WorkspaceNetworkPolicyInterface
type workspaceNetworkPolicies struct {
client rest.Interface
}
// newWorkspaceNetworkPolicies returns a WorkspaceNetworkPolicies
func newWorkspaceNetworkPolicies(c *NetworkV1alpha1Client) *workspaceNetworkPolicies {
return &workspaceNetworkPolicies{
client: c.RESTClient(),
}
}
// Get takes name of the workspaceNetworkPolicy, and returns the corresponding workspaceNetworkPolicy object, and an error if there is any.
func (c *workspaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
result = &v1alpha1.WorkspaceNetworkPolicy{}
err = c.client.Get().
Resource("workspacenetworkpolicies").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of WorkspaceNetworkPolicies that match those selectors.
func (c *workspaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.WorkspaceNetworkPolicyList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.WorkspaceNetworkPolicyList{}
err = c.client.Get().
Resource("workspacenetworkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested workspaceNetworkPolicies.
func (c *workspaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Resource("workspacenetworkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a workspaceNetworkPolicy and creates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any.
func (c *workspaceNetworkPolicies) Create(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
result = &v1alpha1.WorkspaceNetworkPolicy{}
err = c.client.Post().
Resource("workspacenetworkpolicies").
Body(workspaceNetworkPolicy).
Do().
Into(result)
return
}
// Update takes the representation of a workspaceNetworkPolicy and updates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any.
func (c *workspaceNetworkPolicies) Update(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
result = &v1alpha1.WorkspaceNetworkPolicy{}
err = c.client.Put().
Resource("workspacenetworkpolicies").
Name(workspaceNetworkPolicy.Name).
Body(workspaceNetworkPolicy).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *workspaceNetworkPolicies) UpdateStatus(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
result = &v1alpha1.WorkspaceNetworkPolicy{}
err = c.client.Put().
Resource("workspacenetworkpolicies").
Name(workspaceNetworkPolicy.Name).
SubResource("status").
Body(workspaceNetworkPolicy).
Do().
Into(result)
return
}
// Delete takes name of the workspaceNetworkPolicy and deletes it. Returns an error if one occurs.
func (c *workspaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Resource("workspacenetworkpolicies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *workspaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Resource("workspacenetworkpolicies").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched workspaceNetworkPolicy.
func (c *workspaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkspaceNetworkPolicy, err error) {
result = &v1alpha1.WorkspaceNetworkPolicy{}
err = c.client.Patch(pt).
Resource("workspacenetworkpolicies").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@@ -93,8 +93,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
// Group=network.kubesphere.io, Version=v1alpha1
case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil
case networkv1alpha1.SchemeGroupVersion.WithResource("workspacenetworkpolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().WorkspaceNetworkPolicies().Informer()}, nil
// Group=servicemesh.kubesphere.io, Version=v1alpha2
case servicemeshv1alpha2.SchemeGroupVersion.WithResource("servicepolicies"):

View File

@@ -26,8 +26,6 @@ import (
type Interface interface {
// NamespaceNetworkPolicies returns a NamespaceNetworkPolicyInformer.
NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer
// WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer.
WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer
}
type version struct {
@@ -45,8 +43,3 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
func (v *version) NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer {
return &namespaceNetworkPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer.
func (v *version) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer {
return &workspaceNetworkPolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}

View File

@@ -1,88 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
time "time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
)
// WorkspaceNetworkPolicyInformer provides access to a shared informer and lister for
// WorkspaceNetworkPolicies.
type WorkspaceNetworkPolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.WorkspaceNetworkPolicyLister
}
type workspaceNetworkPolicyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// NewWorkspaceNetworkPolicyInformer constructs a new informer for WorkspaceNetworkPolicy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewWorkspaceNetworkPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredWorkspaceNetworkPolicyInformer(client, resyncPeriod, indexers, nil)
}
// NewFilteredWorkspaceNetworkPolicyInformer constructs a new informer for WorkspaceNetworkPolicy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredWorkspaceNetworkPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkV1alpha1().WorkspaceNetworkPolicies().List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkV1alpha1().WorkspaceNetworkPolicies().Watch(options)
},
},
&networkv1alpha1.WorkspaceNetworkPolicy{},
resyncPeriod,
indexers,
)
}
func (f *workspaceNetworkPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredWorkspaceNetworkPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *workspaceNetworkPolicyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&networkv1alpha1.WorkspaceNetworkPolicy{}, f.defaultInformer)
}
func (f *workspaceNetworkPolicyInformer) Lister() v1alpha1.WorkspaceNetworkPolicyLister {
return v1alpha1.NewWorkspaceNetworkPolicyLister(f.Informer().GetIndexer())
}

View File

@@ -25,7 +25,3 @@ type NamespaceNetworkPolicyListerExpansion interface{}
// NamespaceNetworkPolicyNamespaceListerExpansion allows custom methods to be added to
// NamespaceNetworkPolicyNamespaceLister.
type NamespaceNetworkPolicyNamespaceListerExpansion interface{}
// WorkspaceNetworkPolicyListerExpansion allows custom methods to be added to
// WorkspaceNetworkPolicyLister.
type WorkspaceNetworkPolicyListerExpansion interface{}

View File

@@ -1,65 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
// WorkspaceNetworkPolicyLister helps list WorkspaceNetworkPolicies.
type WorkspaceNetworkPolicyLister interface {
// List lists all WorkspaceNetworkPolicies in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.WorkspaceNetworkPolicy, err error)
// Get retrieves the WorkspaceNetworkPolicy from the index for a given name.
Get(name string) (*v1alpha1.WorkspaceNetworkPolicy, error)
WorkspaceNetworkPolicyListerExpansion
}
// workspaceNetworkPolicyLister implements the WorkspaceNetworkPolicyLister interface.
type workspaceNetworkPolicyLister struct {
indexer cache.Indexer
}
// NewWorkspaceNetworkPolicyLister returns a new WorkspaceNetworkPolicyLister.
func NewWorkspaceNetworkPolicyLister(indexer cache.Indexer) WorkspaceNetworkPolicyLister {
return &workspaceNetworkPolicyLister{indexer: indexer}
}
// List lists all WorkspaceNetworkPolicies in the indexer.
func (s *workspaceNetworkPolicyLister) List(selector labels.Selector) (ret []*v1alpha1.WorkspaceNetworkPolicy, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.WorkspaceNetworkPolicy))
})
return ret, err
}
// Get retrieves the WorkspaceNetworkPolicy from the index for a given name.
func (s *workspaceNetworkPolicyLister) Get(name string) (*v1alpha1.WorkspaceNetworkPolicy, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("workspacenetworkpolicy"), name)
}
return obj.(*v1alpha1.WorkspaceNetworkPolicy), nil
}

View File

@@ -34,6 +34,7 @@ const (
IngressControllerPrefix = "kubesphere-router-"
WorkspaceLabelKey = "kubesphere.io/workspace"
NamespaceLabelKey = "kubesphere.io/namespace"
DisplayNameAnnotationKey = "kubesphere.io/alias-name"
DescriptionAnnotationKey = "kubesphere.io/description"
CreatorAnnotationKey = "kubesphere.io/creator"

View File

@@ -117,6 +117,10 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res
// then lets add the finalizer and update the object.
if !sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) {
instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, finalizer)
if instance.Labels == nil {
instance.Labels = make(map[string]string)
}
instance.Labels[constants.NamespaceLabelKey] = instance.Name
if err := r.Update(context.Background(), instance); err != nil {
return reconcile.Result{}, err
}

View File

@@ -1,6 +0,0 @@
package controllerapi
// Controller expose Run method
type Controller interface {
Run(threadiness int, stopCh <-chan struct{}) error
}

View File

@@ -1,5 +1,5 @@
package network
// +kubebuilder:rbac:groups=network.kubesphere.io,resources=workspacenetworkpolicies;namespacenetworkpolicies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups:core,resource=namespaces,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups=network.kubesphere.io,resources=namespacenetworkpolicies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups:core,resource=namespaces,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups:core,resource=services,verbs=get;list;watch;create;update;patch
package network

View File

@@ -2,176 +2,658 @@ package nsnetworkpolicy
import (
"fmt"
"net"
"time"
corev1 "k8s.io/api/core/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
netv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
typev1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
uruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
v1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog"
"k8s.io/klog/klogr"
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
networkinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
workspacev1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
ksnetclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
nspolicy "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
workspace "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
)
const controllerAgentName = "nsnp-controller"
const (
//TODO use set to track service:map
//use period sync service label in NSNP
defaultSleepDuration = 60 * time.Second
type controller struct {
kubeClientset kubernetes.Interface
kubesphereClientset kubesphereclient.Interface
defaultThread = 5
defaultSync = "5m"
nsnpInformer networkinformer.NamespaceNetworkPolicyInformer
nsnpLister networklister.NamespaceNetworkPolicyLister
nsnpSynced cache.InformerSynced
// workqueue is a rate limited work queue. This is used to queue work to be
// processed instead of performing it as soon as a change happens. This
// means we can ensure we only process a fixed amount of resources at a
// time, and makes it easy to ensure we are never processing the same item
// simultaneously in two different workers.
workqueue workqueue.RateLimitingInterface
// recorder is an event recorder for recording Event resources to the
// Kubernetes API.
recorder record.EventRecorder
nsNetworkPolicyProvider provider.NsNetworkPolicyProvider
}
//whether network isolate is enable in namespace
NamespaceNPAnnotationKey = "kubesphere.io/network-isolate"
NamespaceNPAnnotationEnabled = "enabled"
var (
log = klogr.New().WithName("Controller").WithValues("Component", controllerAgentName)
errCount = 0
AnnotationNPNAME = "network-isolate"
//TODO: configure it
DNSLocalIP = "169.254.25.10"
DNSPort = 53
DNSNamespace = "kube-system"
DNSServiceName = "kube-dns"
DNSServiceCoreDNS = "coredns"
)
func NewController(kubeclientset kubernetes.Interface,
kubesphereclientset kubesphereclient.Interface,
nsnpInformer networkinformer.NamespaceNetworkPolicyInformer,
nsNetworkPolicyProvider provider.NsNetworkPolicyProvider) controllerapi.Controller {
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
log.V(4).Info("Creating event broadcaster")
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(klog.Infof)
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")})
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName})
ctl := &controller{
kubeClientset: kubeclientset,
kubesphereClientset: kubesphereclientset,
nsnpInformer: nsnpInformer,
nsnpLister: nsnpInformer.Lister(),
nsnpSynced: nsnpInformer.Informer().HasSynced,
nsNetworkPolicyProvider: nsNetworkPolicyProvider,
// namespacenpController implements the Controller interface for managing kubesphere network policies
// and convery them to k8s NetworkPolicies, then syncing them to the provider.
type NSNetworkPolicyController struct {
client kubernetes.Interface
ksclient ksnetclient.NetworkV1alpha1Interface
informer nspolicy.NamespaceNetworkPolicyInformer
informerSynced cache.InformerSynced
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "NamespaceNetworkPolicies"),
recorder: recorder,
}
log.Info("Setting up event handlers")
nsnpInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.enqueueNSNP,
UpdateFunc: func(old, new interface{}) {
ctl.enqueueNSNP(new)
},
DeleteFunc: ctl.enqueueNSNP,
})
return ctl
//TODO support service in same namespace
serviceInformer v1.ServiceInformer
serviceInformerSynced cache.InformerSynced
nodeInformer v1.NodeInformer
nodeInformerSynced cache.InformerSynced
workspaceInformer workspace.WorkspaceInformer
workspaceInformerSynced cache.InformerSynced
namespaceInformer v1.NamespaceInformer
namespaceInformerSynced cache.InformerSynced
provider provider.NsNetworkPolicyProvider
nsQueue workqueue.RateLimitingInterface
nsnpQueue workqueue.RateLimitingInterface
}
func (c *controller) Run(threadiness int, stopCh <-chan struct{}) error {
defer utilruntime.HandleCrash()
defer c.workqueue.ShutDown()
//init client
// Start the informer factories to begin populating the informer caches
log.V(1).Info("Starting WSNP controller")
// Wait for the caches to be synced before starting workers
log.V(2).Info("Waiting for informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.nsnpSynced); !ok {
return fmt.Errorf("failed to wait for caches to sync")
func stringToCIDR(ipStr string) (string, error) {
cidr := ""
if ip := net.ParseIP(ipStr); ip != nil {
if ip.To4() != nil {
cidr = ipStr + "/32"
} else {
cidr = ipStr + "/128"
}
} else {
return cidr, fmt.Errorf("ip string %s parse error\n", ipStr)
}
log.Info("Starting workers")
// Launch two workers to process Foo resources
for i := 0; i < threadiness; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
return cidr, nil
}
func generateDNSRule(nameServers []string) (netv1.NetworkPolicyEgressRule, error) {
var rule netv1.NetworkPolicyEgressRule
for _, nameServer := range nameServers {
cidr, err := stringToCIDR(nameServer)
if err != nil {
return rule, err
}
rule.To = append(rule.To, netv1.NetworkPolicyPeer{
IPBlock: &netv1.IPBlock{
CIDR: cidr,
},
})
}
protocolTCP := corev1.ProtocolTCP
protocolUDP := corev1.ProtocolUDP
dnsPort := intstr.FromInt(DNSPort)
rule.Ports = append(rule.Ports, netv1.NetworkPolicyPort{
Protocol: &protocolTCP,
Port: &dnsPort,
}, netv1.NetworkPolicyPort{
Protocol: &protocolUDP,
Port: &dnsPort,
})
return rule, nil
}
func (c *NSNetworkPolicyController) generateDNSServiceRule() (netv1.NetworkPolicyEgressRule, error) {
peer, ports, err := c.handlerPeerService(DNSNamespace, DNSServiceName, false)
if err != nil {
peer, ports, err = c.handlerPeerService(DNSNamespace, DNSServiceCoreDNS, false)
}
return netv1.NetworkPolicyEgressRule{
Ports: ports,
To: []netv1.NetworkPolicyPeer{peer},
}, err
}
func (c *NSNetworkPolicyController) handlerPeerService(namespace string, name string, ingress bool) (netv1.NetworkPolicyPeer, []netv1.NetworkPolicyPort, error) {
peerNP := netv1.NetworkPolicyPeer{}
var ports []netv1.NetworkPolicyPort
service, err := c.serviceInformer.Lister().Services(namespace).Get(name)
if err != nil {
return peerNP, nil, err
}
peerNP.PodSelector = new(metav1.LabelSelector)
peerNP.NamespaceSelector = new(metav1.LabelSelector)
if len(service.Spec.Selector) <= 0 {
return peerNP, nil, fmt.Errorf("service %s/%s has no podselect", namespace, name)
}
peerNP.PodSelector.MatchLabels = make(map[string]string)
for key, value := range service.Spec.Selector {
peerNP.PodSelector.MatchLabels[key] = value
}
peerNP.NamespaceSelector.MatchLabels = make(map[string]string)
peerNP.NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace
//only allow traffic to service exposed ports
if !ingress {
ports = make([]netv1.NetworkPolicyPort, 0)
for _, port := range service.Spec.Ports {
portIntString := intstr.FromInt(int(port.Port))
ports = append(ports, netv1.NetworkPolicyPort{
Protocol: &port.Protocol,
Port: &portIntString,
})
}
}
return peerNP, ports, err
}
func (c *NSNetworkPolicyController) convertPeer(peer v1alpha1.NetworkPolicyPeer, ingress bool) (netv1.NetworkPolicyPeer, []netv1.NetworkPolicyPort, error) {
peerNP := netv1.NetworkPolicyPeer{}
var ports []netv1.NetworkPolicyPort
if peer.ServiceSelector != nil {
namespace := peer.ServiceSelector.Namespace
name := peer.ServiceSelector.Name
return c.handlerPeerService(namespace, name, ingress)
} else if peer.NamespaceSelector != nil {
name := peer.NamespaceSelector.Name
peerNP.NamespaceSelector = new(metav1.LabelSelector)
peerNP.NamespaceSelector.MatchLabels = make(map[string]string)
peerNP.NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = name
} else if peer.IPBlock != nil {
peerNP.IPBlock = peer.IPBlock
} else {
klog.Errorf("Invalid nsnp peer %v\n", peer)
return peerNP, nil, fmt.Errorf("Invalid nsnp peer %v\n", peer)
}
return peerNP, ports, nil
}
func (c *NSNetworkPolicyController) convertToK8sNP(n *v1alpha1.NamespaceNetworkPolicy) (*netv1.NetworkPolicy, error) {
np := &netv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: n.Name,
Namespace: n.Namespace,
},
Spec: netv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{},
PolicyTypes: make([]netv1.PolicyType, 0),
},
}
if n.Spec.Egress != nil {
np.Spec.Egress = make([]netv1.NetworkPolicyEgressRule, 0)
for _, egress := range n.Spec.Egress {
tmpRule := netv1.NetworkPolicyEgressRule{}
for _, peer := range egress.To {
peer, ports, err := c.convertPeer(peer, false)
if err != nil {
return nil, err
}
if ports != nil {
np.Spec.Egress = append(np.Spec.Egress, netv1.NetworkPolicyEgressRule{
Ports: ports,
To: []netv1.NetworkPolicyPeer{peer},
})
continue
}
tmpRule.To = append(tmpRule.To, peer)
}
tmpRule.Ports = egress.Ports
if tmpRule.To == nil {
continue
}
np.Spec.Egress = append(np.Spec.Egress, tmpRule)
}
np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, netv1.PolicyTypeEgress)
}
if n.Spec.Ingress != nil {
np.Spec.Ingress = make([]netv1.NetworkPolicyIngressRule, 0)
for _, ingress := range n.Spec.Ingress {
tmpRule := netv1.NetworkPolicyIngressRule{}
for _, peer := range ingress.From {
peer, ports, err := c.convertPeer(peer, true)
if err != nil {
return nil, err
}
if ports != nil {
np.Spec.Ingress = append(np.Spec.Ingress, netv1.NetworkPolicyIngressRule{
Ports: ports,
From: []netv1.NetworkPolicyPeer{peer},
})
}
tmpRule.From = append(tmpRule.From, peer)
}
tmpRule.Ports = ingress.Ports
np.Spec.Ingress = append(np.Spec.Ingress, tmpRule)
}
np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, netv1.PolicyTypeIngress)
}
return np, nil
}
func (c *NSNetworkPolicyController) generateNodeRule() (netv1.NetworkPolicyIngressRule, error) {
var rule netv1.NetworkPolicyIngressRule
nodes, err := c.nodeInformer.Lister().List(labels.Everything())
if err != nil {
return rule, err
}
for _, node := range nodes {
for _, address := range node.Status.Addresses {
cidr, err := stringToCIDR(address.Address)
if err != nil {
klog.V(4).Infof("Error when parse address %s", address.Address)
continue
}
rule.From = append(rule.From, netv1.NetworkPolicyPeer{
IPBlock: &netv1.IPBlock{
CIDR: cidr,
},
})
}
}
return rule, nil
}
func generateNSNP(workspace string, namespace string, matchWorkspace bool) *netv1.NetworkPolicy {
policy := &netv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: AnnotationNPNAME,
Namespace: namespace,
},
Spec: netv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{},
PolicyTypes: make([]netv1.PolicyType, 0),
Ingress: []netv1.NetworkPolicyIngressRule{{
From: []netv1.NetworkPolicyPeer{{
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{},
},
}},
}},
Egress: []netv1.NetworkPolicyEgressRule{{
To: []netv1.NetworkPolicyPeer{{
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{},
},
}},
}},
},
}
policy.Spec.PolicyTypes = append(policy.Spec.PolicyTypes, netv1.PolicyTypeIngress, netv1.PolicyTypeEgress)
if matchWorkspace {
policy.Spec.Ingress[0].From[0].NamespaceSelector.MatchLabels[constants.WorkspaceLabelKey] = workspace
policy.Spec.Egress[0].To[0].NamespaceSelector.MatchLabels[constants.WorkspaceLabelKey] = workspace
} else {
policy.Spec.Ingress[0].From[0].NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace
policy.Spec.Egress[0].To[0].NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace
}
return policy
}
func (c *NSNetworkPolicyController) nsEnqueue(ns *corev1.Namespace) {
key, err := cache.MetaNamespaceKeyFunc(ns)
if err != nil {
uruntime.HandleError(fmt.Errorf("Get namespace key %s failed", ns.Name))
return
}
klog.V(4).Infof("Enqueue namespace %s", ns.Name)
c.nsQueue.Add(key)
}
func (c *NSNetworkPolicyController) addWorkspace(newObj interface{}) {
new := newObj.(*workspacev1alpha1.Workspace)
klog.V(4).Infof("Add workspace %s", new.Name)
label := labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: new.Name})
nsList, err := c.namespaceInformer.Lister().List(label)
if err != nil {
klog.Errorf("Error while list namespace by label %s", label.String())
return
}
for _, ns := range nsList {
c.nsEnqueue(ns)
}
}
func (c *NSNetworkPolicyController) addNamespace(obj interface{}) {
ns := obj.(*corev1.Namespace)
workspaceName := ns.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return
}
klog.V(4).Infof("Add namespace %s", ns.Name)
c.nsEnqueue(ns)
}
func isNetworkIsolateEnabled(ns *corev1.Namespace) bool {
if ns.Annotations[NamespaceNPAnnotationKey] == NamespaceNPAnnotationEnabled {
return true
}
return false
}
func hadNamespaceLabel(ns *corev1.Namespace) bool {
if ns.Annotations[constants.NamespaceLabelKey] == ns.Name {
return true
}
return false
}
func (c *NSNetworkPolicyController) syncNs(key string) error {
klog.V(4).Infof("Sync namespace %s", key)
_, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
klog.Errorf("Not a valid controller key %s, %#v", key, err)
return err
}
ns, err := c.namespaceInformer.Lister().Get(name)
if err != nil {
// not found, possibly been deleted
if errors.IsNotFound(err) {
klog.V(2).Infof("Namespace %v has been deleted", key)
return nil
}
return err
}
workspaceName := ns.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
klog.Error("Workspace name should not be empty")
return nil
}
wksp, err := c.workspaceInformer.Lister().Get(workspaceName)
if err != nil {
//Should not be here
if errors.IsNotFound(err) {
klog.V(2).Infof("Workspace %v has been deleted", workspaceName)
return nil
}
return err
}
//Maybe some ns not labeled
if !hadNamespaceLabel(ns) {
ns.Labels[constants.NamespaceLabelKey] = ns.Name
_, err := c.client.CoreV1().Namespaces().Update(ns)
if err != nil {
//Just log, label can also be added by namespace controller
klog.Errorf("cannot label namespace %s", ns.Name)
}
}
matchWorkspace := false
delete := false
if isNetworkIsolateEnabled(ns) {
matchWorkspace = false
} else if wksp.Spec.NetworkIsolation {
matchWorkspace = true
} else {
delete = true
}
policy := generateNSNP(workspaceName, ns.Name, matchWorkspace)
ruleDNS, err := generateDNSRule([]string{DNSLocalIP})
if err != nil {
return err
}
policy.Spec.Egress = append(policy.Spec.Egress, ruleDNS)
ruleDNSService, err := c.generateDNSServiceRule()
if err == nil {
policy.Spec.Egress = append(policy.Spec.Egress, ruleDNSService)
} else {
klog.Warningf("Cannot handle service %s or %s", DNSServiceName, DNSServiceCoreDNS)
}
ruleNode, err := c.generateNodeRule()
if err != nil {
return err
}
policy.Spec.Ingress = append(policy.Spec.Ingress, ruleNode)
if delete {
c.provider.Delete(c.provider.GetKey(AnnotationNPNAME, ns.Name))
//delete all namespace np when networkisolate not active
if c.ksclient.NamespaceNetworkPolicies(ns.Name).DeleteCollection(nil, typev1.ListOptions{}) != nil {
klog.Errorf("Error when delete all nsnps in namespace %s", ns.Name)
}
} else {
err = c.provider.Set(policy)
if err != nil {
klog.Errorf("Error while converting %#v to provider's network policy.", policy)
return err
}
}
klog.V(2).Info("Started workers")
<-stopCh
log.V(2).Info("Shutting down workers")
return nil
}
func (c *controller) enqueueNSNP(obj interface{}) {
var key string
var err error
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
utilruntime.HandleError(err)
return
}
c.workqueue.Add(key)
}
func (c *controller) runWorker() {
for c.processNextWorkItem() {
func (c *NSNetworkPolicyController) nsWorker() {
for c.processNsWorkItem() {
}
}
func (c *controller) processNextWorkItem() bool {
obj, shutdown := c.workqueue.Get()
if shutdown {
func (c *NSNetworkPolicyController) processNsWorkItem() bool {
key, quit := c.nsQueue.Get()
if quit {
return false
}
defer c.nsQueue.Done(key)
// We wrap this block in a func so we can defer c.workqueue.Done.
err := func(obj interface{}) error {
// We call Done here so the workqueue knows we have finished
// processing this item. We also must remember to call Forget if we
// do not want this work item being re-queued. For example, we do
// not call Forget if a transient error occurs, instead the item is
// put back on the workqueue and attempted again after a back-off
// period.
defer c.workqueue.Done(obj)
var key string
var ok bool
// We expect strings to come off the workqueue. These are of the
// form namespace/name. We do this as the delayed nature of the
// workqueue means the items in the informer cache may actually be
// more up to date that when the item was initially put onto the
// workqueue.
if key, ok = obj.(string); !ok {
// As the item in the workqueue is actually invalid, we call
// Forget here else we'd go into a loop of attempting to
// process a work item that is invalid.
c.workqueue.Forget(obj)
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
return nil
}
// Run the reconcile, passing it the namespace/name string of the
// Foo resource to be synced.
if err := c.reconcile(key); err != nil {
// Put the item back on the workqueue to handle any transient errors.
c.workqueue.AddRateLimited(key)
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
}
// Finally, if no error occurs we Forget this item so it does not
// get queued again until another change happens.
c.workqueue.Forget(obj)
log.Info("Successfully synced", "key", key)
return nil
}(obj)
if err != nil {
utilruntime.HandleError(err)
return true
if err := c.syncNs(key.(string)); err != nil {
klog.Errorf("Error when syncns %s", err)
}
return true
}
func (c *NSNetworkPolicyController) nsnpEnqueue(obj interface{}) {
nsnp := obj.(*v1alpha1.NamespaceNetworkPolicy)
key, err := cache.MetaNamespaceKeyFunc(nsnp)
if err != nil {
uruntime.HandleError(fmt.Errorf("get namespace network policy key %s failed", nsnp.Name))
return
}
c.nsnpQueue.Add(key)
}
func (c *NSNetworkPolicyController) syncNSNP(key string) error {
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
klog.Errorf("Not a valid controller key %s, %#v", key, err)
return err
}
nsnp, err := c.informer.Lister().NamespaceNetworkPolicies(namespace).Get(name)
if err != nil {
if errors.IsNotFound(err) {
klog.V(4).Infof("NSNP %v has been deleted", key)
c.provider.Delete(c.provider.GetKey(name, namespace))
return nil
}
return err
}
np, err := c.convertToK8sNP(nsnp)
if err != nil {
klog.Errorf("Error while convert nsnp to k8snp: %s", err)
return err
}
err = c.provider.Set(np)
if err != nil {
return err
}
return nil
}
func (c *NSNetworkPolicyController) nsNPWorker() {
for c.processNSNPWorkItem() {
}
}
func (c *NSNetworkPolicyController) processNSNPWorkItem() bool {
key, quit := c.nsnpQueue.Get()
if quit {
return false
}
defer c.nsnpQueue.Done(key)
c.syncNSNP(key.(string))
return true
}
// NewnamespacenpController returns a controller which manages NSNSP objects.
func NewNSNetworkPolicyController(
client kubernetes.Interface,
ksclient ksnetclient.NetworkV1alpha1Interface,
nsnpInformer nspolicy.NamespaceNetworkPolicyInformer,
serviceInformer v1.ServiceInformer,
nodeInformer v1.NodeInformer,
workspaceInformer workspace.WorkspaceInformer,
namespaceInformer v1.NamespaceInformer,
policyProvider provider.NsNetworkPolicyProvider) *NSNetworkPolicyController {
controller := &NSNetworkPolicyController{
client: client,
ksclient: ksclient,
informer: nsnpInformer,
informerSynced: nsnpInformer.Informer().HasSynced,
serviceInformer: serviceInformer,
serviceInformerSynced: serviceInformer.Informer().HasSynced,
nodeInformer: nodeInformer,
nodeInformerSynced: nodeInformer.Informer().HasSynced,
workspaceInformer: workspaceInformer,
workspaceInformerSynced: workspaceInformer.Informer().HasSynced,
namespaceInformer: namespaceInformer,
namespaceInformerSynced: namespaceInformer.Informer().HasSynced,
provider: policyProvider,
nsQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
nsnpQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespacenp"),
}
workspaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.addWorkspace,
UpdateFunc: func(oldObj, newObj interface{}) {
old := oldObj.(*workspacev1alpha1.Workspace)
new := oldObj.(*workspacev1alpha1.Workspace)
if old.Spec.NetworkIsolation == new.Spec.NetworkIsolation {
return
}
controller.addWorkspace(newObj)
},
})
namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.addNamespace,
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
old := oldObj.(*corev1.Namespace)
new := oldObj.(*corev1.Namespace)
if old.Annotations[NamespaceNPAnnotationKey] == new.Annotations[NamespaceNPAnnotationKey] {
return
}
controller.addNamespace(newObj)
},
})
nsnpInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
klog.V(4).Infof("Got ADD event for NSNSP: %#v", obj)
controller.nsnpEnqueue(obj)
},
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
klog.V(4).Info("Got UPDATE event for NSNSP.")
klog.V(4).Infof("Old object: \n%#v\n", oldObj)
klog.V(4).Infof("New object: \n%#v\n", newObj)
controller.nsnpEnqueue(newObj)
},
DeleteFunc: func(obj interface{}) {
klog.V(4).Infof("Got DELETE event for NSNP: %#v", obj)
controller.nsnpEnqueue(obj)
},
}, defaultSleepDuration)
return controller
}
func (c *NSNetworkPolicyController) Start(stopCh <-chan struct{}) error {
return c.Run(defaultThread, defaultSync, stopCh)
}
// Run starts the controller.
func (c *NSNetworkPolicyController) Run(threadiness int, reconcilerPeriod string, stopCh <-chan struct{}) error {
defer uruntime.HandleCrash()
defer c.nsQueue.ShutDown()
defer c.nsnpQueue.ShutDown()
klog.Info("Waiting to sync with Kubernetes API (NSNP)")
if ok := cache.WaitForCacheSync(stopCh, c.informerSynced, c.serviceInformerSynced, c.workspaceInformerSynced, c.namespaceInformerSynced, c.nodeInformerSynced); !ok {
return fmt.Errorf("Failed to wait for caches to sync")
}
klog.Info("Finished syncing with Kubernetes API (NSNP)")
// Start a number of worker threads to read from the queue. Each worker
// will pull keys off the resource cache event queue and sync them to the
// K8s datastore.
for i := 0; i < threadiness; i++ {
go wait.Until(c.nsWorker, time.Second, stopCh)
go wait.Until(c.nsNPWorker, time.Second, stopCh)
}
//Work to sync K8s NetworkPolicy
go c.provider.Start(stopCh)
klog.Info("NSNP controller is now running")
<-stopCh
klog.Info("Stopping NSNP controller")
return nil
}

View File

@@ -11,8 +11,7 @@ import (
func TestNsnetworkpolicy(t *testing.T) {
klog.InitFlags(nil)
flag.Set("logtostderr", "false")
flag.Set("alsologtostderr", "false")
flag.Set("logtostderr", "true")
flag.Set("v", "4")
flag.Parse()
klog.SetOutput(GinkgoWriter)

View File

@@ -1,90 +1,337 @@
package nsnetworkpolicy
import (
"fmt"
"reflect"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
nsnplister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
corev1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/util/yaml"
kubeinformers "k8s.io/client-go/informers"
informerv1 "k8s.io/client-go/informers/core/v1"
kubefake "k8s.io/client-go/kubernetes/fake"
"k8s.io/klog"
netv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
wkspv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
ksfake "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
nsnppolicyinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
workspaceinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing"
)
var (
fakeControllerBuilder *controllertesting.FakeControllerBuilder
c controllerapi.Controller
stopCh chan struct{}
calicoProvider *provider.FakeCalicoNetworkProvider
nsnpLister nsnplister.NamespaceNetworkPolicyLister
c *NSNetworkPolicyController
stopCh chan struct{}
nsnpInformer nsnppolicyinformer.NamespaceNetworkPolicyInformer
serviceInformer informerv1.ServiceInformer
workspaceInformer workspaceinformer.WorkspaceInformer
namespaceInformer informerv1.NamespaceInformer
alwaysReady = func() bool { return true }
)
const (
workspaceNP = `
apiVersion: "networking.k8s.io/v1"
kind: "NetworkPolicy"
metadata:
name: networkisolate
namespace: %s
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
%s: %s
Egress:
- To:
- namespaceSelector:
matchLabels:
%s: %s
policyTypes:
- Ingress
- Egress`
serviceTmp = `
apiVersion: v1
kind: Service
metadata:
name: myservice
namespace: testns
spec:
clusterIP: 10.0.0.1
selector:
app: mylbapp
ports:
- protocol: TCP
port: 80
targetPort: 9376
`
workspaceTmp = `
apiVersion: tenant.kubesphere.io/v1alpha1
kind: Workspace
metadata:
annotations:
kubesphere.io/creator: admin
name: testworkspace
spec:
manager: admin
networkIsolation: true
status: {}
`
nsTmp = `
apiVersion: v1
kind: Namespace
metadata:
labels:
kubesphere.io/workspace: testworkspace
name: testns
spec:
finalizers:
- kubernetes
`
)
func StringToObject(data string, obj interface{}) error {
reader := strings.NewReader(data)
return yaml.NewYAMLOrJSONDecoder(reader, 10).Decode(obj)
}
var _ = Describe("Nsnetworkpolicy", func() {
BeforeEach(func() {
fakeControllerBuilder = controllertesting.NewFakeControllerBuilder()
stopCh = make(chan struct{})
informer, _ := fakeControllerBuilder.NewControllerInformer()
calicoProvider = provider.NewFakeCalicoNetworkProvider()
c = NewController(fakeControllerBuilder.KubeClient, fakeControllerBuilder.KsClient, informer.Network().V1alpha1().NamespaceNetworkPolicies(), calicoProvider)
go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stopCh)
originalController := c.(*controller)
originalController.recorder = &record.FakeRecorder{}
go c.Run(1, stopCh)
nsnpLister = informer.Network().V1alpha1().NamespaceNetworkPolicies().Lister()
calicoProvider := provider.NewFakeNetworkProvider()
kubeClient := kubefake.NewSimpleClientset()
ksClient := ksfake.NewSimpleClientset()
kubeInformer := kubeinformers.NewSharedInformerFactory(kubeClient, 0)
ksInformer := ksinformers.NewSharedInformerFactory(ksClient, 0)
nsnpInformer := ksInformer.Network().V1alpha1().NamespaceNetworkPolicies()
serviceInformer := kubeInformer.Core().V1().Services()
nodeInforemer := kubeInformer.Core().V1().Nodes()
workspaceInformer := ksInformer.Tenant().V1alpha1().Workspaces()
namespaceInformer := kubeInformer.Core().V1().Namespaces()
c = NewNSNetworkPolicyController(kubeClient, ksClient.NetworkV1alpha1(), nsnpInformer, serviceInformer, nodeInforemer, workspaceInformer, namespaceInformer, calicoProvider)
serviceObj := &corev1.Service{}
Expect(StringToObject(serviceTmp, serviceObj)).ShouldNot(HaveOccurred())
Expect(serviceInformer.Informer().GetIndexer().Add(serviceObj)).ShouldNot(HaveOccurred())
nsObj := &corev1.Namespace{}
Expect(StringToObject(nsTmp, nsObj)).ShouldNot(HaveOccurred())
namespaceInformer.Informer().GetIndexer().Add(nsObj)
workspaceObj := &wkspv1alpha1.Workspace{}
Expect(StringToObject(workspaceTmp, workspaceObj)).ShouldNot(HaveOccurred())
workspaceInformer.Informer().GetIndexer().Add(workspaceObj)
c.namespaceInformerSynced = alwaysReady
c.serviceInformerSynced = alwaysReady
c.workspaceInformerSynced = alwaysReady
c.informerSynced = alwaysReady
go c.Start(stopCh)
})
It("Should create a new calico object", func() {
objSrt := `{
"apiVersion": "network.kubesphere.io/v1alpha1",
"kind": "NetworkPolicy",
"metadata": {
"name": "allow-tcp-6379",
"namespace": "production"
},
"spec": {
"selector": "color == 'red'",
"ingress": [
{
"action": "Allow",
"protocol": "TCP",
"source": {
"selector": "color == 'blue'"
},
"destination": {
"ports": [
6379
]
}
}
]
}
}`
obj := &v1alpha1.NamespaceNetworkPolicy{}
Expect(controllertesting.StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
_, err := fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Create(obj)
It("Should create ns networkisolate np correctly in workspace", func() {
objSrt := fmt.Sprintf(workspaceNP, "testns", constants.WorkspaceLabelKey, "testworkspace", constants.WorkspaceLabelKey, "testworkspace")
obj := &netv1.NetworkPolicy{}
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
policy := generateNSNP("testworkspace", "testns", true)
Expect(reflect.DeepEqual(obj.Spec, policy.Spec)).To(BeTrue())
})
It("Should create ns networkisolate np correctly in ns", func() {
objSrt := fmt.Sprintf(workspaceNP, "testns", constants.NamespaceLabelKey, "testns", constants.NamespaceLabelKey, "testns")
obj := &netv1.NetworkPolicy{}
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
policy := generateNSNP("testworkspace", "testns", false)
Expect(reflect.DeepEqual(obj.Spec, policy.Spec)).To(BeTrue())
})
It("test func convertToK8sNP", func() {
objSrt := `
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: namespaceIPblockNP
namespace: testns
spec:
ingress:
- from:
- ipBlock:
cidr: 172.0.0.1/16
ports:
- protocol: TCP
port: 80
`
obj := &netv1alpha1.NamespaceNetworkPolicy{}
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
policy, err := c.convertToK8sNP(obj)
objSrt = `
apiVersion: "networking.k8s.io/v1"
kind: "NetworkPolicy"
metadata:
name: IPblockNP
namespace: testns
spec:
ingress:
- from:
- ipBlock:
cidr: 172.0.0.1/16
ports:
- protocol: TCP
port: 80
policyTypes:
- Ingress
`
obj2 := &netv1.NetworkPolicy{}
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
Expect(err).ShouldNot(HaveOccurred())
Eventually(func() bool {
exist, _ := calicoProvider.CheckExist(obj)
return exist
}).Should(BeTrue())
obj, _ = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Get(obj.Name, metav1.GetOptions{})
Expect(obj.Finalizers).To(HaveLen(1))
// TestUpdate
newStr := "color == 'green'"
obj.Spec.Selector = newStr
_, err = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
Expect(reflect.DeepEqual(obj2.Spec, policy.Spec)).To(BeTrue())
})
It("test func convertToK8sNP with namespace", func() {
objSrt := `
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: testnamespace
namespace: testns2
spec:
ingress:
- from:
- namespace:
name: testns
`
obj := &netv1alpha1.NamespaceNetworkPolicy{}
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
np, err := c.convertToK8sNP(obj)
Expect(err).ShouldNot(HaveOccurred())
Eventually(func() string {
o, err := calicoProvider.Get(obj)
if err != nil {
return err.Error()
}
n := o.(*v1alpha1.NamespaceNetworkPolicy)
return n.Spec.Selector
}).Should(Equal(newStr))
// TestDelete
Expect(fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Delete(obj.Name, &metav1.DeleteOptions{})).ShouldNot(HaveOccurred())
objTmp := `
apiVersion: "networking.k8s.io/v1"
kind: "NetworkPolicy"
metadata:
name: testnamespace
namespace: testns2
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
%s: %s
policyTypes:
- Ingress`
objSrt = fmt.Sprintf(objTmp, constants.NamespaceLabelKey, "testns")
obj2 := &netv1.NetworkPolicy{}
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue())
})
It("test func convertToK8sNP with service ingress", func() {
objSrt := `
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: testnamespace
namespace: testns2
spec:
ingress:
- from:
- service:
name: myservice
namespace: testns
`
obj := &netv1alpha1.NamespaceNetworkPolicy{}
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
np, err := c.convertToK8sNP(obj)
Expect(err).ShouldNot(HaveOccurred())
objSrt = `
apiVersion: "networking.k8s.io/v1"
kind: NetworkPolicy
metadata:
name: networkisolate
namespace: testns
spec:
podSelector: {}
ingress:
- from:
- podSelector:
matchLabels:
app: mylbapp
namespaceSelector:
matchLabels:
kubesphere.io/namespace: testns
policyTypes:
- Ingress
`
obj2 := &netv1.NetworkPolicy{}
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
klog.Errorf("\n%v\n%v\n", np.Spec, obj2.Spec)
Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue())
})
It("test func convertToK8sNP with service egress", func() {
objSrt := `
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: testnamespace
namespace: testns2
spec:
egress:
- To:
- service:
name: myservice
namespace: testns
`
obj := &netv1alpha1.NamespaceNetworkPolicy{}
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
np, err := c.convertToK8sNP(obj)
Expect(err).ShouldNot(HaveOccurred())
objSrt = `
apiVersion: "networking.k8s.io/v1"
kind: NetworkPolicy
metadata:
name: networkisolate
namespace: testns
spec:
podSelector: {}
egress:
- to:
- podSelector:
matchLabels:
app: mylbapp
namespaceSelector:
matchLabels:
kubesphere.io/namespace: testns
ports:
- protocol: TCP
port: 80
policyTypes:
- Egress
`
obj2 := &netv1.NetworkPolicy{}
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
klog.Errorf("\n%v\n%v\n", np.Spec, obj2.Spec)
Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue())
})
AfterEach(func() {

View File

@@ -1,119 +0,0 @@
package nsnetworkpolicy
import (
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/utils"
)
const (
controllerFinalizier = "nsnp.finalizers.networking.kubesphere.io"
)
var clog logr.Logger
func (c *controller) reconcile(key string) error {
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return err
}
clog = log.WithValues("name", name, "namespace", namespace)
clog.V(1).Info("---------Begin to reconcile--------")
defer clog.V(1).Info("---------Reconcile done--------")
obj, err := c.nsnpLister.NamespaceNetworkPolicies(namespace).Get(name)
if err != nil {
if errors.IsNotFound(err) {
clog.V(2).Info("Object is removed")
return nil
}
clog.Error(err, "Failed to get resource")
return err
}
stop, err := c.addOrRemoveFinalizer(obj)
if err != nil {
return err
}
if stop {
return nil
}
clog.V(2).Info("Check if we need a create or update")
ok, err := c.nsNetworkPolicyProvider.CheckExist(obj)
if err != nil {
clog.Error(err, "Failed to check exist of network policy")
return err
}
if !ok {
clog.V(1).Info("Create a new object in backend")
err = c.nsNetworkPolicyProvider.Add(obj)
if err != nil {
clog.Error(err, "Failed to create np")
return err
}
return nil
}
needUpdate, err := c.nsNetworkPolicyProvider.NeedUpdate(obj)
if err != nil {
clog.Error(err, "Failed to check if object need a update")
return err
}
if needUpdate {
clog.V(1).Info("Update object in backend")
err = c.nsNetworkPolicyProvider.Update(obj)
if err != nil {
clog.Error(err, "Failed to update object")
return err
}
}
return nil
}
func (c *controller) addOrRemoveFinalizer(obj *v1alpha1.NamespaceNetworkPolicy) (bool, error) {
if obj.ObjectMeta.DeletionTimestamp.IsZero() {
if !utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) {
clog.V(2).Info("Detect no finalizer")
obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, controllerFinalizier)
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
_, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
return err
})
if err != nil {
clog.Error(err, "Failed to add finalizer")
return false, err
}
return false, nil
}
} else {
// The object is being deleted
if utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) {
// our finalizer is present, so lets handle any external dependency
if err := c.deleteProviderNSNP(obj); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return false, err
}
clog.V(2).Info("Removing finalizer")
// remove our finalizer from the list and update it.
obj.ObjectMeta.Finalizers = utils.RemoveString(obj.ObjectMeta.Finalizers, controllerFinalizier)
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
_, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
return err
})
if err != nil {
clog.Error(err, "Failed to remove finalizer")
return false, err
}
return true, nil
}
}
return false, nil
}
// deleteProviderNSNP delete network policy in the backend
func (c *controller) deleteProviderNSNP(obj *v1alpha1.NamespaceNetworkPolicy) error {
clog.V(2).Info("Deleting backend network policy")
return c.nsNetworkPolicyProvider.Delete(obj)
}

View File

@@ -0,0 +1,38 @@
package nsnetworkpolicy
import (
"context"
"fmt"
"net/http"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
// +kubebuilder:webhook:path=/validate-service-nsnp-kubesphere-io-v1alpha1-network,name=validate-v1-service,mutating=false,failurePolicy=fail,groups="",resources=services,verbs=create;update,versions=v1
// serviceValidator validates service
type ServiceValidator struct {
decoder *admission.Decoder
}
// Service must hash label, becasue nsnp will use it
func (v *ServiceValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
service := &corev1.Service{}
err := v.decoder.Decode(req, service)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
if service.Spec.Selector == nil {
return admission.Denied(fmt.Sprintf("missing label"))
}
return admission.Allowed("")
}
func (a *ServiceValidator) InjectDecoder(d *admission.Decoder) error {
a.decoder = d
return nil
}

View File

@@ -1,3 +0,0 @@
package provider
// +kubebuilder:rbac:groups="crd.projectcalico.org",resources=globalfelixconfigs;felixconfigurations;ippools;ipamblocks;globalnetworkpolicies;globalnetworksets;networkpolicies;networksets;clusterinformations;hostendpoints,verbs=get;list;watch;create;patch;update;delete

View File

@@ -0,0 +1,49 @@
package provider
import (
"fmt"
"github.com/projectcalico/kube-controllers/pkg/converter"
api "github.com/projectcalico/libcalico-go/lib/apis/v3"
constants "github.com/projectcalico/libcalico-go/lib/backend/k8s/conversion"
v1 "k8s.io/api/networking/v1"
)
func NewFakeNetworkProvider() *FakeNetworkProvider {
f := new(FakeNetworkProvider)
f.NSNPData = make(map[string]*api.NetworkPolicy)
f.policyConverter = converter.NewPolicyConverter()
return f
}
type FakeNetworkProvider struct {
NSNPData map[string]*api.NetworkPolicy
policyConverter converter.Converter
}
func (f *FakeNetworkProvider) Delete(key string) {
delete(f.NSNPData, key)
}
func (f *FakeNetworkProvider) Start(stopCh <-chan struct{}) {
}
func (f *FakeNetworkProvider) Set(np *v1.NetworkPolicy) error {
policy, err := f.policyConverter.Convert(np)
if err != nil {
return err
}
// Add to cache.
k := f.policyConverter.GetKey(policy)
tmp := policy.(api.NetworkPolicy)
f.NSNPData[k] = &tmp
return nil
}
func (f *FakeNetworkProvider) GetKey(name, nsname string) string {
policyName := fmt.Sprintf(constants.K8sNetworkPolicyNamePrefix + name)
return fmt.Sprintf("%s/%s", nsname, policyName)
}

View File

@@ -1,66 +0,0 @@
package provider
import (
"reflect"
"github.com/projectcalico/libcalico-go/lib/errors"
"k8s.io/client-go/tools/cache"
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
func NewFakeCalicoNetworkProvider() *FakeCalicoNetworkProvider {
f := new(FakeCalicoNetworkProvider)
f.NSNPData = make(map[string]*api.NamespaceNetworkPolicy)
return f
}
type FakeCalicoNetworkProvider struct {
NSNPData map[string]*api.NamespaceNetworkPolicy
}
func (f *FakeCalicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
obj, ok := f.NSNPData[namespacename]
if !ok {
return nil, errors.ErrorResourceDoesNotExist{}
}
return obj, nil
}
func (f *FakeCalicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
if _, ok := f.NSNPData[namespacename]; ok {
return errors.ErrorResourceAlreadyExists{}
}
f.NSNPData[namespacename] = o
return nil
}
func (f *FakeCalicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
if _, ok := f.NSNPData[namespacename]; ok {
return true, nil
}
return false, nil
}
func (f *FakeCalicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
store := f.NSNPData[namespacename]
if !reflect.DeepEqual(store, o) {
return true, nil
}
return false, nil
}
func (f *FakeCalicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
f.NSNPData[namespacename] = o
return nil
}
func (f *FakeCalicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
delete(f.NSNPData, namespacename)
return nil
}

View File

@@ -1 +0,0 @@
package provider

View File

@@ -1,35 +1,11 @@
package provider
import (
k8snetworkinformer "k8s.io/client-go/informers/networking/v1"
k8snetworklister "k8s.io/client-go/listers/networking/v1"
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
import netv1 "k8s.io/api/networking/v1"
// NsNetworkPolicyProvider is a interface to let different cnis to implement our api
type NsNetworkPolicyProvider interface {
Add(*api.NamespaceNetworkPolicy) error
CheckExist(*api.NamespaceNetworkPolicy) (bool, error)
NeedUpdate(*api.NamespaceNetworkPolicy) (bool, error)
Update(*api.NamespaceNetworkPolicy) error
Delete(*api.NamespaceNetworkPolicy) error
Get(*api.NamespaceNetworkPolicy) (interface{}, error)
}
// TODO: support no-calico CNI
type k8sNetworkProvider struct {
networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer
networkPolicyLister k8snetworklister.NetworkPolicyLister
}
func (k *k8sNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
return nil
}
func (k *k8sNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
return false, nil
}
func (k *k8sNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
return nil
Delete(key string)
Set(policy *netv1.NetworkPolicy) error
Start(stopCh <-chan struct{})
GetKey(name, nsname string) string
}

View File

@@ -1,144 +0,0 @@
package provider
import (
"context"
"encoding/json"
"reflect"
"time"
v3 "github.com/projectcalico/libcalico-go/lib/apis/v3"
"github.com/projectcalico/libcalico-go/lib/clientv3"
"github.com/projectcalico/libcalico-go/lib/errors"
"github.com/projectcalico/libcalico-go/lib/options"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/klogr"
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
var log = klogr.New().WithName("calico-client")
var defaultBackoff = wait.Backoff{
Steps: 4,
Duration: 10 * time.Millisecond,
Factor: 5.0,
Jitter: 0.1,
}
type calicoNetworkProvider struct {
np clientv3.NetworkPolicyInterface
}
func NewCalicoNetworkProvider(np clientv3.NetworkPolicyInterface) NsNetworkPolicyProvider {
return &calicoNetworkProvider{
np: np,
}
}
func convertSpec(n *api.NamespaceNetworkPolicySpec) *v3.NetworkPolicySpec {
bytes, err := json.Marshal(&n)
if err != nil {
panic(err)
}
m := new(v3.NetworkPolicySpec)
err = json.Unmarshal(bytes, m)
if err != nil {
panic(err)
}
return m
}
// ConvertAPIToCalico convert our api to calico api
func ConvertAPIToCalico(n *api.NamespaceNetworkPolicy) *v3.NetworkPolicy {
output := v3.NewNetworkPolicy()
//Object Metadata
output.ObjectMeta.Name = n.Name
output.Namespace = n.Namespace
output.Annotations = n.Annotations
output.Labels = n.Labels
//spec
output.Spec = *(convertSpec(&n.Spec))
return output
}
func (k *calicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) {
return k.np.Get(context.TODO(), o.Namespace, o.Name, options.GetOptions{})
}
func (k *calicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
log.V(3).Info("Creating network policy", "name", o.Name, "namespace", o.Namespace)
obj := ConvertAPIToCalico(o)
log.V(4).Info("Show object spe detail", "name", o.Name, "namespace", o.Namespace, "Spec", obj.Spec)
_, err := k.np.Create(context.TODO(), obj, options.SetOptions{})
return err
}
func (k *calicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
log.V(3).Info("Checking network policy whether exsits or not", "name", o.Name, "namespace", o.Namespace)
out, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{})
if err != nil {
if _, ok := err.(errors.ErrorResourceDoesNotExist); ok {
return false, nil
}
return false, err
}
if out != nil {
return true, nil
}
return false, nil
}
func (k *calicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
log.V(3).Info("Deleting network policy", "name", o.Name, "namespace", o.Namespace)
_, err := k.np.Delete(context.Background(), o.Namespace, o.Name, options.DeleteOptions{})
return err
}
func (k *calicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) {
store, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{})
if err != nil {
log.Error(err, "Failed to get resource", "name", o.Name, "namespace", o.Namespace)
}
expected := ConvertAPIToCalico(o)
log.V(4).Info("Comparing Spec", "store", store.Spec, "current", expected.Spec)
if !reflect.DeepEqual(store.Spec, expected.Spec) {
return true, nil
}
return false, nil
}
func (k *calicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error {
log.V(3).Info("Updating network policy", "name", o.Name, "namespace", o.Namespace)
updateObject, err := k.Get(o)
if err != nil {
log.Error(err, "Failed to get resource in store")
return err
}
up := updateObject.(*v3.NetworkPolicy)
up.Spec = *convertSpec(&o.Spec)
err = RetryOnConflict(defaultBackoff, func() error {
_, err := k.np.Update(context.Background(), up, options.SetOptions{})
return err
})
if err != nil {
log.Error(err, "Failed to update resource", "name", o.Name, "namespace", o.Namespace)
}
return err
}
// RetryOnConflict is same as the function in k8s, but replaced with error in calico
func RetryOnConflict(backoff wait.Backoff, fn func() error) error {
var lastConflictErr error
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
err := fn()
if err == nil {
return true, nil
}
if _, ok := err.(errors.ErrorResourceUpdateConflict); ok {
lastConflictErr = err
return false, nil
}
return false, err
})
if err == wait.ErrWaitTimeout {
err = lastConflictErr
}
return err
}

View File

@@ -0,0 +1,250 @@
package provider
import (
"context"
"fmt"
"reflect"
"strings"
"sync"
"time"
rcache "github.com/projectcalico/kube-controllers/pkg/cache"
netv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
uruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
informerv1 "k8s.io/client-go/informers/networking/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/klog"
)
const (
defaultSyncTime = 5 * time.Minute
)
func (c *k8sPolicyController) GetKey(name, nsname string) string {
return fmt.Sprintf("%s/%s", nsname, name)
}
func getkey(key string) (string, string) {
strs := strings.Split(key, "/")
return strs[0], strs[1]
}
// policyController implements the Controller interface for managing Kubernetes network policies
// and syncing them to the k8s datastore as NetworkPolicies.
type k8sPolicyController struct {
client kubernetes.Interface
informer informerv1.NetworkPolicyInformer
ctx context.Context
resourceCache rcache.ResourceCache
hasSynced cache.InformerSynced
}
func (c *k8sPolicyController) Start(stopCh <-chan struct{}) {
c.run(5, "5m", stopCh)
}
func (c *k8sPolicyController) Set(np *netv1.NetworkPolicy) error {
klog.V(4).Infof("Set NetworkPolicy %s/%s %+v", np.Namespace, np.Name, np)
// Add to cache.
k := c.GetKey(np.Name, np.Namespace)
c.resourceCache.Set(k, *np)
return nil
}
func (c *k8sPolicyController) Delete(key string) {
klog.V(4).Infof("Delete NetworkPolicy %s", key)
c.resourceCache.Delete(key)
}
// Run starts the controller.
func (c *k8sPolicyController) run(threadiness int, reconcilerPeriod string, stopCh <-chan struct{}) {
defer uruntime.HandleCrash()
// Let the workers stop when we are done
workqueue := c.resourceCache.GetQueue()
defer workqueue.ShutDown()
// Wait until we are in sync with the Kubernetes API before starting the
// resource cache.
klog.Info("Waiting to sync with Kubernetes API (NetworkPolicy)")
if ok := cache.WaitForCacheSync(stopCh, c.hasSynced); !ok {
}
klog.Infof("Finished syncing with Kubernetes API (NetworkPolicy)")
// Start the resource cache - this will trigger the queueing of any keys
// that are out of sync onto the resource cache event queue.
c.resourceCache.Run(reconcilerPeriod)
// Start a number of worker threads to read from the queue. Each worker
// will pull keys off the resource cache event queue and sync them to the
// k8s datastore.
for i := 0; i < threadiness; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
}
klog.Info("NetworkPolicy controller is now running")
<-stopCh
klog.Info("Stopping NetworkPolicy controller")
}
func (c *k8sPolicyController) runWorker() {
for c.processNextItem() {
}
}
// processNextItem waits for an event on the output queue from the resource cache and syncs
// any received keys to the datastore.
func (c *k8sPolicyController) processNextItem() bool {
// Wait until there is a new item in the work queue.
workqueue := c.resourceCache.GetQueue()
key, quit := workqueue.Get()
if quit {
return false
}
// Sync the object to the k8s datastore.
if err := c.syncToDatastore(key.(string)); err != nil {
c.handleErr(err, key.(string))
}
// Indicate that we're done processing this key, allowing for safe parallel processing such that
// two objects with the same key are never processed in parallel.
workqueue.Done(key)
return true
}
// syncToDatastore syncs the given update to the k8s datastore. The provided key can be used to
// find the corresponding resource within the resource cache. If the resource for the provided key
// exists in the cache, then the value should be written to the datastore. If it does not exist
// in the cache, then it should be deleted from the datastore.
func (c *k8sPolicyController) syncToDatastore(key string) error {
// Check if it exists in the controller's cache.
obj, exists := c.resourceCache.Get(key)
if !exists {
// The object no longer exists - delete from the datastore.
klog.Infof("Deleting NetworkPolicy %s from k8s datastore", key)
ns, name := getkey(key)
err := c.client.NetworkingV1().NetworkPolicies(ns).Delete(name, nil)
if errors.IsNotFound(err) {
return nil
}
return err
} else {
// The object exists - update the datastore to reflect.
klog.Infof("Create/Update NetworkPolicy %s in k8s datastore", key)
p := obj.(netv1.NetworkPolicy)
// Lookup to see if this object already exists in the datastore.
gp, err := c.informer.Lister().NetworkPolicies(p.Namespace).Get(p.Name)
if err != nil {
if !errors.IsNotFound(err) {
klog.Warningf("Failed to get NetworkPolicy %s from datastore", key)
return err
}
// Doesn't exist - create it.
_, err := c.client.NetworkingV1().NetworkPolicies(p.Namespace).Create(&p)
if err != nil {
klog.Warningf("Failed to create NetworkPolicy %s", key)
return err
}
klog.Infof("Successfully created NetworkPolicy %s", key)
return nil
}
klog.V(4).Infof("New NetworkPolicy %s/%s %+v\n", p.Namespace, p.Name, p.Spec)
klog.V(4).Infof("Old NetworkPolicy %s/%s %+v\n", gp.Namespace, gp.Name, gp.Spec)
// The policy already exists, update it and write it back to the datastore.
gp.Spec = p.Spec
_, err = c.client.NetworkingV1().NetworkPolicies(p.Namespace).Update(gp)
if err != nil {
klog.Warningf("Failed to update NetworkPolicy %s", key)
return err
}
klog.Infof("Successfully updated NetworkPolicy %s", key)
return nil
}
}
// handleErr handles errors which occur while processing a key received from the resource cache.
// For a given error, we will re-queue the key in order to retry the datastore sync up to 5 times,
// at which point the update is dropped.
func (c *k8sPolicyController) handleErr(err error, key string) {
workqueue := c.resourceCache.GetQueue()
if err == nil {
// Forget about the #AddRateLimited history of the key on every successful synchronization.
// This ensures that future processing of updates for this key is not delayed because of
// an outdated error history.
workqueue.Forget(key)
return
}
// This controller retries 5 times if something goes wrong. After that, it stops trying.
if workqueue.NumRequeues(key) < 5 {
// Re-enqueue the key rate limited. Based on the rate limiter on the
// queue and the re-enqueue history, the key will be processed later again.
klog.Errorf("Error syncing NetworkPolicy %v: %v", key, err)
workqueue.AddRateLimited(key)
return
}
workqueue.Forget(key)
// Report to an external entity that, even after several retries, we could not successfully process this key
uruntime.HandleError(err)
klog.Errorf("Dropping NetworkPolicy %q out of the queue: %v", key, err)
}
//NewNsNetworkPolicyProvider sync k8s NetworkPolicy
func NewNsNetworkPolicyProvider(client kubernetes.Interface, npInformer informerv1.NetworkPolicyInformer) (NsNetworkPolicyProvider, error) {
var once sync.Once
c := &k8sPolicyController{
client: client,
informer: npInformer,
ctx: context.Background(),
hasSynced: npInformer.Informer().HasSynced,
}
// Function returns map of policyName:policy stored by policy controller
// in datastore.
listFunc := func() (map[string]interface{}, error) {
//Wait cache be set by NSNP Controller, otherwise NetworkPolicy will be delete
//by mistake
once.Do(func() {
time.Sleep(defaultSyncTime)
})
// Get all policies from datastore
//TODO filter np not belong to kubesphere
policies, err := npInformer.Lister().List(labels.Everything())
if err != nil {
return nil, err
}
// Filter in only objects that are written by policy controller.
m := make(map[string]interface{})
for _, policy := range policies {
policy.ObjectMeta = metav1.ObjectMeta{Name: policy.Name, Namespace: policy.Namespace}
k := c.GetKey(policy.Name, policy.Namespace)
m[k] = *policy
}
klog.Infof("Found %d policies in k8s datastore:", len(m))
return m, nil
}
cacheArgs := rcache.ResourceCacheArgs{
ListFunc: listFunc,
ObjectType: reflect.TypeOf(netv1.NetworkPolicy{}),
}
c.resourceCache = rcache.NewResourceCache(cacheArgs)
return c, nil
}

View File

@@ -1,80 +0,0 @@
package runoption
import (
"time"
"github.com/projectcalico/libcalico-go/lib/apiconfig"
"github.com/projectcalico/libcalico-go/lib/clientv3"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
ksinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
)
const (
certPath = "/calicocerts"
KubernetesDataStore = "k8s"
EtcdDataStore = "etcd"
)
type RunOption struct {
ProviderName string
DataStoreType string
EtcdEndpoints string
AllowInsecureEtcd bool
}
func (r RunOption) Run() error {
klog.V(1).Info("Check config")
if err := r.check(); err != nil {
return err
}
klog.V(1).Info("Preparing kubernetes client")
config, err := rest.InClusterConfig()
if err != nil {
panic(err.Error())
}
// creates the clientset
k8sClientset := kubernetes.NewForConfigOrDie(config)
ksClientset := versioned.NewForConfigOrDie(config)
informer := ksinformer.NewSharedInformerFactory(ksClientset, time.Minute*10)
klog.V(1).Info("Kubernetes client initialized successfully")
var npProvider provider.NsNetworkPolicyProvider
if r.ProviderName == "calico" {
klog.V(1).Info("Preparing calico client")
config := apiconfig.NewCalicoAPIConfig()
config.Spec.EtcdEndpoints = r.EtcdEndpoints
if !r.AllowInsecureEtcd {
config.Spec.EtcdKeyFile = certPath + "/etcd-key"
config.Spec.EtcdCertFile = certPath + "/etcd-cert"
config.Spec.EtcdCACertFile = certPath + "/etcd-ca"
}
if r.DataStoreType == KubernetesDataStore {
config.Spec.DatastoreType = apiconfig.Kubernetes
} else {
config.Spec.DatastoreType = apiconfig.EtcdV3
}
client, err := clientv3.New(*config)
if err != nil {
klog.Fatal("Failed to initialize calico client", err)
}
npProvider = provider.NewCalicoNetworkProvider(client.NetworkPolicies())
klog.V(1).Info("Calico client initialized successfully")
}
//TODO: support no-calico cni
c := nsnetworkpolicy.NewController(k8sClientset, ksClientset, informer.Network().V1alpha1().NamespaceNetworkPolicies(), npProvider)
stop := make(chan struct{})
klog.V(1).Infof("Starting controller")
go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stop)
return c.Run(1, stop)
}
func (r RunOption) check() error {
return nil
}

View File

@@ -1,38 +0,0 @@
package testing
import (
"time"
"k8s.io/apimachinery/pkg/runtime"
kubeinformers "k8s.io/client-go/informers"
k8sfake "k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
)
var (
AlwaysReady = func() bool { return true }
ResyncPeriodFunc = func() time.Duration { return 1 * time.Second }
)
type FakeControllerBuilder struct {
KsClient *fake.Clientset
KubeClient *k8sfake.Clientset
Kubeobjects []runtime.Object
CRDObjects []runtime.Object
}
func NewFakeControllerBuilder() *FakeControllerBuilder {
return &FakeControllerBuilder{
Kubeobjects: make([]runtime.Object, 0),
CRDObjects: make([]runtime.Object, 0),
}
}
func (f *FakeControllerBuilder) NewControllerInformer() (informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) {
f.KsClient = fake.NewSimpleClientset(f.CRDObjects...)
f.KubeClient = k8sfake.NewSimpleClientset(f.Kubeobjects...)
i := informers.NewSharedInformerFactory(f.KsClient, ResyncPeriodFunc())
k8sI := kubeinformers.NewSharedInformerFactory(f.KubeClient, ResyncPeriodFunc())
return i, k8sI
}

View File

@@ -1,12 +0,0 @@
package testing
import (
"strings"
"k8s.io/apimachinery/pkg/util/yaml"
)
func StringToObject(data string, obj interface{}) error {
reader := strings.NewReader(data)
return yaml.NewYAMLOrJSONDecoder(reader, 10).Decode(obj)
}

View File

@@ -1,22 +0,0 @@
package utils
// ContainsString report if s is in a slice
func ContainsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}
// RemoveString remove s from slice if exists
func RemoveString(slice []string, s string) (result []string) {
for _, item := range slice {
if item == s {
continue
}
result = append(result, item)
}
return
}

View File

@@ -1,280 +0,0 @@
package wsnetworkpolicy
import (
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
k8snetwork "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
corev1informer "k8s.io/client-go/informers/core/v1"
k8snetworkinformer "k8s.io/client-go/informers/networking/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
corev1lister "k8s.io/client-go/listers/core/v1"
k8snetworklister "k8s.io/client-go/listers/networking/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog"
"k8s.io/klog/klogr"
workspaceapi "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
networkinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
workspaceinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
workspacelister "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
)
const controllerAgentName = "wsnp-controller"
var (
log = klogr.New().WithName("Controller").WithValues(controllerAgentName)
errCount = 0
)
type controller struct {
kubeClientset kubernetes.Interface
kubesphereClientset kubesphereclient.Interface
wsnpInformer networkinformer.WorkspaceNetworkPolicyInformer
wsnpLister networklister.WorkspaceNetworkPolicyLister
wsnpSynced cache.InformerSynced
networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer
networkPolicyLister k8snetworklister.NetworkPolicyLister
networkPolicySynced cache.InformerSynced
namespaceLister corev1lister.NamespaceLister
namespaceInformer corev1informer.NamespaceInformer
namespaceSynced cache.InformerSynced
workspaceLister workspacelister.WorkspaceLister
workspaceInformer workspaceinformer.WorkspaceInformer
workspaceSynced cache.InformerSynced
// workqueue is a rate limited work queue. This is used to queue work to be
// processed instead of performing it as soon as a change happens. This
// means we can ensure we only process a fixed amount of resources at a
// time, and makes it easy to ensure we are never processing the same item
// simultaneously in two different workers.
workqueue workqueue.RateLimitingInterface
// recorder is an event recorder for recording Event resources to the
// Kubernetes API.
recorder record.EventRecorder
}
func NewController(kubeclientset kubernetes.Interface,
kubesphereclientset kubesphereclient.Interface,
wsnpInformer networkinformer.WorkspaceNetworkPolicyInformer,
networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer,
namespaceInformer corev1informer.NamespaceInformer,
workspaceInformer workspaceinformer.WorkspaceInformer) controllerapi.Controller {
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
log.V(4).Info("Creating event broadcaster")
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(klog.Infof)
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")})
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName})
ctl := &controller{
kubeClientset: kubeclientset,
kubesphereClientset: kubesphereclientset,
wsnpInformer: wsnpInformer,
wsnpLister: wsnpInformer.Lister(),
wsnpSynced: wsnpInformer.Informer().HasSynced,
networkPolicyInformer: networkPolicyInformer,
networkPolicyLister: networkPolicyInformer.Lister(),
networkPolicySynced: networkPolicyInformer.Informer().HasSynced,
namespaceInformer: namespaceInformer,
namespaceLister: namespaceInformer.Lister(),
namespaceSynced: namespaceInformer.Informer().HasSynced,
workspaceInformer: workspaceInformer,
workspaceLister: workspaceInformer.Lister(),
workspaceSynced: workspaceInformer.Informer().HasSynced,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceNetworkPolicies"),
recorder: recorder,
}
log.Info("Setting up event handlers")
wsnpInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.enqueueWSNP,
UpdateFunc: func(old, new interface{}) {
ctl.enqueueWSNP(new)
},
})
networkPolicyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.handleNP,
UpdateFunc: func(old, new interface{}) {
newNP := new.(*k8snetwork.NetworkPolicy)
oldNP := old.(*k8snetwork.NetworkPolicy)
if newNP.ResourceVersion == oldNP.ResourceVersion {
return
}
ctl.handleNP(new)
},
DeleteFunc: ctl.handleNP,
})
workspaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.handleWS,
UpdateFunc: func(old, new interface{}) {
newNP := new.(*workspaceapi.Workspace)
oldNP := old.(*workspaceapi.Workspace)
if newNP.ResourceVersion == oldNP.ResourceVersion {
return
}
ctl.handleWS(new)
},
DeleteFunc: ctl.handleNP,
})
return ctl
}
func (c *controller) handleWS(obj interface{}) {
ws := obj.(*workspaceapi.Workspace)
wsnps, err := c.wsnpLister.List(labels.Everything())
if err != nil {
log.Error(err, "Failed to get WSNP when a workspace changed ")
return
}
for _, wsnp := range wsnps {
log.V(4).Info("Enqueue wsnp because a workspace being changed", "obj", ws.Name)
c.enqueueWSNP(wsnp)
}
return
}
func (c *controller) Run(threadiness int, stopCh <-chan struct{}) error {
defer utilruntime.HandleCrash()
defer c.workqueue.ShutDown()
// Start the informer factories to begin populating the informer caches
log.Info("Starting WSNP controller")
// Wait for the caches to be synced before starting workers
log.Info("Waiting for informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.wsnpSynced, c.namespaceSynced, c.networkPolicySynced, c.workspaceSynced); !ok {
return fmt.Errorf("failed to wait for caches to sync")
}
log.Info("Starting workers")
// Launch two workers to process Foo resources
for i := 0; i < threadiness; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
}
klog.Info("Started workers")
<-stopCh
log.Info("Shutting down workers")
return nil
}
func (c *controller) enqueueWSNP(obj interface{}) {
var key string
var err error
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
utilruntime.HandleError(err)
return
}
c.workqueue.Add(key)
}
func (c *controller) handleNP(obj interface{}) {
var object metav1.Object
var ok bool
if object, ok = obj.(metav1.Object); !ok {
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
if !ok {
utilruntime.HandleError(fmt.Errorf("error decoding object, invalid type"))
return
}
object, ok = tombstone.Obj.(metav1.Object)
if !ok {
utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type"))
return
}
log.V(4).Info("Recovered deleted object from tombstone", "name", object.GetName())
}
log.V(4).Info("Processing object:", "name", object.GetName())
if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
if ownerRef.Kind != "WorkspaceNetworkPol" {
return
}
wsnp, err := c.wsnpLister.Get(ownerRef.Name)
if err != nil {
log.V(4).Info("ignoring orphaned object", "link", object.GetSelfLink(), "name", ownerRef.Name)
return
}
c.enqueueWSNP(wsnp)
return
}
}
func (c *controller) runWorker() {
for c.processNextWorkItem() {
}
}
func (c *controller) processNextWorkItem() bool {
obj, shutdown := c.workqueue.Get()
if shutdown {
return false
}
// We wrap this block in a func so we can defer c.workqueue.Done.
err := func(obj interface{}) error {
// We call Done here so the workqueue knows we have finished
// processing this item. We also must remember to call Forget if we
// do not want this work item being re-queued. For example, we do
// not call Forget if a transient error occurs, instead the item is
// put back on the workqueue and attempted again after a back-off
// period.
defer c.workqueue.Done(obj)
var key string
var ok bool
// We expect strings to come off the workqueue. These are of the
// form namespace/name. We do this as the delayed nature of the
// workqueue means the items in the informer cache may actually be
// more up to date that when the item was initially put onto the
// workqueue.
if key, ok = obj.(string); !ok {
// As the item in the workqueue is actually invalid, we call
// Forget here else we'd go into a loop of attempting to
// process a work item that is invalid.
c.workqueue.Forget(obj)
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
return nil
}
// Run the reconcile, passing it the namespace/name string of the
// Foo resource to be synced.
if err := c.reconcile(key); err != nil {
// Put the item back on the workqueue to handle any transient errors.
c.workqueue.AddRateLimited(key)
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
}
// Finally, if no error occurs we Forget this item so it does not
// get queued again until another change happens.
c.workqueue.Forget(obj)
log.Info("Successfully synced", key)
return nil
}(obj)
if err != nil {
utilruntime.HandleError(err)
return true
}
return true
}
func (c *controller) handleError(err error) {
log.Error(err, "Error in handling")
errCount++
}

View File

@@ -1,203 +0,0 @@
package wsnetworkpolicy
import (
"fmt"
"reflect"
"sort"
corev1 "k8s.io/api/core/v1"
ks8network "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
errutil "k8s.io/apimachinery/pkg/util/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry"
wsnpapi "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
const (
workspaceSelectorLabel = "kubesphere.io/workspace"
workspaceNetworkPolicyLabel = "networking.kubesphere.io/wsnp"
MessageResourceExists = "Resource %q already exists and is not managed by WorkspaceNetworkPolicy"
ErrResourceExists = "ErrResourceExists"
)
var everything = labels.Everything()
var reconcileCount = 0
// NetworkPolicyNameForWSNP return the name of the networkpolicy owned by this WNSP
func NetworkPolicyNameForWSNP(wsnp string) string {
return wsnp + "-np"
}
func (c *controller) reconcile(key string) error {
reconcileCount++
_, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key))
return nil
}
olog := log.WithName(name)
olog.Info("Begin to reconcile")
owner, err := c.wsnpLister.Get(name)
if err != nil {
if errors.IsNotFound(err) {
utilruntime.HandleError(fmt.Errorf("WSNP '%s' in work queue no longer exists", key))
return nil
}
return err
}
namespaces, err := c.listNamespacesInWorkspace(owner.Spec.Workspace)
if err != nil {
return err
}
var errs []error
for _, ns := range namespaces {
err = c.reconcileNamespace(ns.Name, owner)
if err != nil {
errs = append(errs, err)
}
}
if len(errs) == 0 {
return nil
}
return errutil.NewAggregate(errs)
}
func (c *controller) reconcileNamespace(name string, wsnp *wsnpapi.WorkspaceNetworkPolicy) error {
npname := NetworkPolicyNameForWSNP(wsnp.Name)
np, err := c.generateNPForNamesapce(name, wsnp)
if err != nil {
log.Error(nil, "Failed to generate NetworkPolicy", "wsnp", wsnp, "namespace", name)
return err
}
old, err := c.networkPolicyLister.NetworkPolicies(name).Get(npname)
if errors.IsNotFound(err) {
_, err = c.kubeClientset.NetworkingV1().NetworkPolicies(name).Create(np)
if err != nil {
log.Error(err, "cannot create networkpolicy of this wsnp", wsnp)
return err
}
return nil
}
if err != nil {
log.Error(err, "Failed to get networkPolicy")
return err
}
if !metav1.IsControlledBy(old, wsnp) {
msg := fmt.Sprintf(MessageResourceExists, old.Name)
c.recorder.Event(wsnp, corev1.EventTypeWarning, ErrResourceExists, msg)
return fmt.Errorf(msg)
}
if !reflect.DeepEqual(old.Spec, np.Spec) {
log.V(2).Info("Detect network policy changed, updating network policy", "the old one", old.Spec, "the new one", np.Spec)
err = retry.RetryOnConflict(retry.DefaultBackoff, func() error {
_, err = c.kubeClientset.NetworkingV1().NetworkPolicies(name).Update(np)
return err
})
if err != nil {
log.Error(err, "Failed to update wsnp")
return err
}
log.V(2).Info("updating completed")
}
return nil
}
func (c *controller) generateNPForNamesapce(ns string, wsnp *wsnpapi.WorkspaceNetworkPolicy) (*ks8network.NetworkPolicy, error) {
np := &ks8network.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: NetworkPolicyNameForWSNP(wsnp.Name),
Namespace: ns,
Labels: map[string]string{workspaceNetworkPolicyLabel: wsnp.Name},
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(wsnp, wsnpapi.SchemeGroupVersion.WithKind("WorkspaceNetworkPolicy")),
},
},
Spec: ks8network.NetworkPolicySpec{
PolicyTypes: wsnp.Spec.PolicyTypes,
},
}
if wsnp.Spec.Ingress != nil {
np.Spec.Ingress = make([]ks8network.NetworkPolicyIngressRule, len(wsnp.Spec.Ingress))
for index, ing := range wsnp.Spec.Ingress {
ingRule, err := c.transformWSNPIngressToK8sIngress(ing)
if err != nil {
return nil, err
}
np.Spec.Ingress[index] = *ingRule
}
}
return np, nil
}
func (c *controller) transformWSNPIngressToK8sIngress(rule wsnpapi.WorkspaceNetworkPolicyIngressRule) (*ks8network.NetworkPolicyIngressRule, error) {
k8srule := &ks8network.NetworkPolicyIngressRule{
Ports: rule.Ports,
From: make([]ks8network.NetworkPolicyPeer, len(rule.From)),
}
for index, f := range rule.From {
k8srule.From[index] = f.NetworkPolicyPeer
if f.WorkspaceSelector != nil {
if f.WorkspaceSelector.Size() == 0 {
k8srule.From[index].NamespaceSelector = &metav1.LabelSelector{}
} else {
selector, err := metav1.LabelSelectorAsSelector(f.WorkspaceSelector)
if err != nil {
log.Error(err, "Failed to convert label selectors")
return nil, err
}
ws, err := c.workspaceLister.List(selector)
if err != nil {
log.Error(err, "Failed to list workspaces")
return nil, err
}
if len(ws) == 0 {
log.Info("ws selector doesnot match anything")
continue
}
if k8srule.From[index].NamespaceSelector == nil {
k8srule.From[index].NamespaceSelector = &metav1.LabelSelector{}
}
if len(ws) == 1 {
if k8srule.From[index].NamespaceSelector.MatchLabels == nil {
k8srule.From[index].NamespaceSelector.MatchLabels = make(map[string]string)
}
k8srule.From[index].NamespaceSelector.MatchLabels[workspaceSelectorLabel] = ws[0].Name
} else {
if k8srule.From[index].NamespaceSelector.MatchExpressions == nil {
k8srule.From[index].NamespaceSelector.MatchExpressions = make([]metav1.LabelSelectorRequirement, 0)
}
re := metav1.LabelSelectorRequirement{
Key: workspaceSelectorLabel,
Operator: metav1.LabelSelectorOpIn,
Values: make([]string, len(ws)),
}
for index, w := range ws {
re.Values[index] = w.Name
}
sort.Strings(re.Values)
k8srule.From[index].NamespaceSelector.MatchExpressions = append(k8srule.From[index].NamespaceSelector.MatchExpressions, re)
}
}
}
}
return k8srule, nil
}
func (c *controller) listNamespacesInWorkspace(workspace string) ([]*corev1.Namespace, error) {
selector, err := labels.Parse(workspaceSelectorLabel + "==" + workspace)
if err != nil {
log.Error(err, "Failed to parse label selector")
return nil, err
}
namespaces, err := c.namespaceLister.List(selector)
if err != nil {
log.Error(err, "Failed to list namespaces in this workspace")
return nil, err
}
return namespaces, nil
}

View File

@@ -1,21 +0,0 @@
package wsnetworkpolicy
import (
"flag"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/klog"
)
func TestWsnetworkpolicy(t *testing.T) {
klog.InitFlags(nil)
flag.Set("logtostderr", "false")
flag.Set("alsologtostderr", "false")
flag.Set("v", "4")
flag.Parse()
klog.SetOutput(GinkgoWriter)
RegisterFailHandler(Fail)
RunSpecs(t, "Wsnetworkpolicy Suite")
}

View File

@@ -1,242 +0,0 @@
package wsnetworkpolicy
import (
"fmt"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
corev1 "k8s.io/api/core/v1"
k8snetwork "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
netv1lister "k8s.io/client-go/listers/networking/v1"
"k8s.io/client-go/tools/record"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
tenant "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing"
)
var (
fakeControllerBuilder *controllertesting.FakeControllerBuilder
c controllerapi.Controller
npLister netv1lister.NetworkPolicyLister
stopCh chan struct{}
deletePolicy metav1.DeletionPropagation
testName string
)
var _ = Describe("Wsnetworkpolicy", func() {
BeforeEach(func() {
deletePolicy = metav1.DeletePropagationBackground
fakeControllerBuilder = controllertesting.NewFakeControllerBuilder()
informer, k8sinformer := fakeControllerBuilder.NewControllerInformer()
stopCh = make(chan struct{})
c = NewController(fakeControllerBuilder.KubeClient, fakeControllerBuilder.KsClient,
informer.Network().V1alpha1().WorkspaceNetworkPolicies(), k8sinformer.Networking().V1().NetworkPolicies(),
k8sinformer.Core().V1().Namespaces(), informer.Tenant().V1alpha1().Workspaces())
originalController := c.(*controller)
go originalController.wsnpInformer.Informer().Run(stopCh)
go originalController.networkPolicyInformer.Informer().Run(stopCh)
go originalController.namespaceInformer.Informer().Run(stopCh)
go originalController.workspaceInformer.Informer().Run(stopCh)
originalController.recorder = &record.FakeRecorder{}
go c.Run(1, stopCh)
npLister = k8sinformer.Networking().V1().NetworkPolicies().Lister()
testName = "test"
ns1 := newWorkspaceNamespaces("ns1", testName)
ns2 := newWorkspaceNamespaces("ns2", testName)
_, err := fakeControllerBuilder.KubeClient.CoreV1().Namespaces().Create(ns1)
Expect(err).ShouldNot(HaveOccurred())
_, err = fakeControllerBuilder.KubeClient.CoreV1().Namespaces().Create(ns2)
Expect(err).ShouldNot(HaveOccurred())
})
AfterEach(func() {
close(stopCh)
})
It("Should proper ingress rule when using workspaceSelector", func() {
label := map[string]string{"workspace": "test-selector"}
ws := &tenant.Workspace{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Labels: label,
},
}
_, err := fakeControllerBuilder.KsClient.TenantV1alpha1().Workspaces().Create(ws)
wsnp := newWorkspaceNP(testName)
wsnp.Spec.PolicyTypes = []k8snetwork.PolicyType{k8snetwork.PolicyTypeIngress}
wsnp.Spec.Ingress = []v1alpha1.WorkspaceNetworkPolicyIngressRule{
{
From: []v1alpha1.WorkspaceNetworkPolicyPeer{
{
WorkspaceSelector: &metav1.LabelSelector{
MatchLabels: label,
},
},
},
}}
_, err = fakeControllerBuilder.KsClient.NetworkV1alpha1().WorkspaceNetworkPolicies().Create(wsnp)
Expect(err).ShouldNot(HaveOccurred())
expect1Json := `{
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "test-np",
"namespace": "ns1",
"labels": {
"networking.kubesphere.io/wsnp": "test"
}
},
"spec": {
"policyTypes": [
"Ingress"
],
"ingress": [
{
"from": [
{
"namespaceSelector": {
"matchLabels": {
"kubesphere.io/workspace": "test"
}
}
}
]
}
]
}
}`
expect1 := &k8snetwork.NetworkPolicy{}
Expect(controllertesting.StringToObject(expect1Json, expect1)).ShouldNot(HaveOccurred())
nps := []*k8snetwork.NetworkPolicy{}
Eventually(func() error {
selector, _ := labels.Parse(workspaceNetworkPolicyLabel + "==test")
nps, err = npLister.List(selector)
if err != nil {
klog.Errorf("Failed to list npmerr:%s", err.Error())
return err
}
if len(nps) != 2 {
return fmt.Errorf("Length is not right, current length :%d", len(nps))
}
return nil
}, time.Second*5, time.Second).ShouldNot(HaveOccurred())
for _, np := range nps {
Expect(np.Labels).To(Equal(expect1.Labels))
Expect(np.Spec).To(Equal(expect1.Spec))
}
// create a new ws will change the `From`
ws2 := &tenant.Workspace{
ObjectMeta: metav1.ObjectMeta{
Name: "test2",
Labels: label,
},
}
_, err = fakeControllerBuilder.KsClient.TenantV1alpha1().Workspaces().Create(ws2)
Expect(err).ShouldNot(HaveOccurred())
expect2Json := `{
"apiVersion": "networking.k8s.io/v1",
"kind": "NetworkPolicy",
"metadata": {
"name": "test-np",
"namespace": "ns1",
"labels": {
"networking.kubesphere.io/wsnp": "test"
}
},
"spec": {
"policyTypes": [
"Ingress"
],
"ingress": [
{
"from": [
{
"namespaceSelector": {
"matchExpressions": [{
"key": "kubesphere.io/workspace",
"operator":"In",
"values": ["test", "test2"]
}]
}
}
]
}
]
}
}`
expect2 := &k8snetwork.NetworkPolicy{}
Expect(controllertesting.StringToObject(expect2Json, expect2)).ShouldNot(HaveOccurred())
id := func(element interface{}) string {
e := element.(*k8snetwork.NetworkPolicy)
return e.Namespace
}
Eventually(func() []*k8snetwork.NetworkPolicy {
selector, _ := labels.Parse(workspaceNetworkPolicyLabel + "=test")
nps, err := npLister.List(selector)
if err != nil {
return nil
}
if len(nps) != 2 {
klog.Errorf("Length is not right, current length :%d", len(nps))
return nil
}
return nps
}, time.Second*5, time.Second).Should(MatchAllElements(id, Elements{
"ns1": PointTo(MatchFields(IgnoreExtras, Fields{
"Spec": Equal(expect2.Spec),
})),
"ns2": PointTo(MatchFields(IgnoreExtras, Fields{
"Spec": Equal(expect2.Spec),
})),
}))
})
It("Should create networkpolicies", func() {
//create a wsnp
_, err := fakeControllerBuilder.KsClient.NetworkV1alpha1().WorkspaceNetworkPolicies().Create(newWorkspaceNP(testName))
Expect(err).ShouldNot(HaveOccurred())
Eventually(func() error {
selector, _ := labels.Parse(workspaceNetworkPolicyLabel + "=" + testName)
nps, err := npLister.List(selector)
if err != nil {
return err
}
if len(nps) != 2 {
return fmt.Errorf("Length is not right, current length :%d", len(nps))
}
return nil
}, time.Second*5, time.Second).ShouldNot(HaveOccurred())
err = fakeControllerBuilder.KsClient.NetworkV1alpha1().WorkspaceNetworkPolicies().Delete(testName, &metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
})
Expect(err).ShouldNot(HaveOccurred())
})
})
func newWorkspaceNP(name string) *v1alpha1.WorkspaceNetworkPolicy {
return &v1alpha1.WorkspaceNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1alpha1.WorkspaceNetworkPolicySpec{
Workspace: name,
},
}
}
func newWorkspaceNamespaces(ns, ws string) *corev1.Namespace {
return &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
Labels: map[string]string{workspaceSelectorLabel: ws},
},
}
}

View File

@@ -18,7 +18,6 @@ import (
"k8s.io/kube-openapi/pkg/common"
devopsinstall "kubesphere.io/kubesphere/pkg/apis/devops/crdinstall"
devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
networkinstall "kubesphere.io/kubesphere/pkg/apis/network/crdinstall"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
servicemeshinstall "kubesphere.io/kubesphere/pkg/apis/servicemesh/crdinstall"
@@ -77,9 +76,9 @@ func main() {
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceSingularS2iBinary),
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBinary), meta.RESTScopeRoot)
mapper.AddSpecific(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindWorkspaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralWorkspaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourceSingularWorkspaceNetworkPolicy), meta.RESTScopeRoot)
mapper.AddSpecific(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindNamespaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralNamespaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourceSingularNamespaceNetworkPolicy), meta.RESTScopeRoot)
mapper.AddSpecific(devopsv1alpha3.SchemeGroupVersion.WithKind(devopsv1alpha3.ResourceKindDevOpsProject),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourceSingularDevOpsProject), meta.RESTScopeRoot)
@@ -138,7 +137,7 @@ func main() {
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iRun),
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilderTemplate),
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilder),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralWorkspaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralNamespaceNetworkPolicy),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralPipeline),
clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesPluralAgent),

View File

@@ -1,513 +0,0 @@
// Copyright 2014 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "cloud.google.com/go/compute/metadata"
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"time"
)
const (
// metadataIP is the documented metadata server IP address.
metadataIP = "169.254.169.254"
// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv = "GCE_METADATA_HOST"
userAgent = "gcloud-golang/0.1"
)
type cachedValue struct {
k string
trim bool
mu sync.Mutex
v string
}
var (
projID = &cachedValue{k: "project/project-id", trim: true}
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
instID = &cachedValue{k: "instance/id", trim: true}
)
var (
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
)
// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string
func (suffix NotDefinedError) Error() string {
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}
func (c *cachedValue) get(cl *Client) (v string, err error) {
defer c.mu.Unlock()
c.mu.Lock()
if c.v != "" {
return c.v, nil
}
if c.trim {
v, err = cl.getTrimmed(c.k)
} else {
v, err = cl.Get(c.k)
}
if err == nil {
c.v = v
}
return
}
var (
onGCEOnce sync.Once
onGCE bool
)
// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
onGCEOnce.Do(initOnGCE)
return onGCE
}
func initOnGCE() {
onGCE = testOnGCE()
}
func testOnGCE() bool {
// The user explicitly said they're on GCE, so trust them.
if os.Getenv(metadataHostEnv) != "" {
return true
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
resc := make(chan bool, 2)
// Try two strategies in parallel.
// See https://github.com/googleapis/google-cloud-go/issues/194
go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent)
res, err := defaultClient.hc.Do(req.WithContext(ctx))
if err != nil {
resc <- false
return
}
defer res.Body.Close()
resc <- res.Header.Get("Metadata-Flavor") == "Google"
}()
go func() {
addrs, err := net.LookupHost("metadata.google.internal")
if err != nil || len(addrs) == 0 {
resc <- false
return
}
resc <- strsContains(addrs, metadataIP)
}()
tryHarder := systemInfoSuggestsGCE()
if tryHarder {
res := <-resc
if res {
// The first strategy succeeded, so let's use it.
return true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case res = <-resc:
return res
case <-timer.C:
// Too slow. Who knows what this system is.
return false
}
}
// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return <-resc
}
// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func systemInfoSuggestsGCE() bool {
if runtime.GOOS != "linux" {
// We don't have any non-Linux clues available, at least yet.
return false
}
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
name := strings.TrimSpace(string(slurp))
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
}
// Get calls Client.Get on the default client.
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return defaultClient.ProjectID() }
// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) { return defaultClient.InternalIP() }
// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) { return defaultClient.Hostname() }
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) { return defaultClient.InstanceID() }
// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) { return defaultClient.InstanceName() }
// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) { return defaultClient.Zone() }
// InstanceAttributes calls Client.InstanceAttributes on the default client.
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
// ProjectAttributes calls Client.ProjectAttributes on the default client.
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValue(attr)
}
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValue(attr)
}
// Scopes calls Client.Scopes on the default client.
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
// A Client provides metadata.
type Client struct {
hc *http.Client
}
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
func NewClient(c *http.Client) *Client {
return &Client{hc: c}
}
// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
u := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", u, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
if res.StatusCode != 200 {
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
}
return string(all), res.Header.Get("Etag"), nil
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func (c *Client) Get(suffix string) (string, error) {
val, _, err := c.getETag(suffix)
return val, err
}
func (c *Client) getTrimmed(suffix string) (s string, err error) {
s, err = c.Get(suffix)
s = strings.TrimSpace(s)
return
}
func (c *Client) lines(suffix string) ([]string, error) {
j, err := c.Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}
// ProjectID returns the current instance's project ID string.
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
// NumericProjectID returns the current instance's numeric project ID.
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
// InstanceID returns the current VM's numeric instance ID.
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
// InternalIP returns the instance's primary internal IP address.
func (c *Client) InternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/ip")
}
// ExternalIP returns the instance's primary external (public) IP address.
func (c *Client) ExternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func (c *Client) Hostname() (string, error) {
return c.getTrimmed("instance/hostname")
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func (c *Client) InstanceTags() ([]string, error) {
var s []string
j, err := c.Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}
// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func (c *Client) Zone() (string, error) {
zone, err := c.getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
return c.Get("instance/attributes/" + attr)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
return c.Get("project/attributes/" + attr)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
}
// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
const failedSubscribeSleep = time.Second * 5
// First check to see if the metadata value exists at all.
val, lastETag, err := c.getETag(suffix)
if err != nil {
return err
}
if err := fn(val, true); err != nil {
return err
}
ok := true
if strings.ContainsRune(suffix, '?') {
suffix += "&wait_for_change=true&last_etag="
} else {
suffix += "?wait_for_change=true&last_etag="
}
for {
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil {
if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep)
continue // Retry on other errors.
}
ok = false
}
lastETag = etag
if err := fn(val, ok); err != nil || !ok {
return err
}
}
}
// Error contains an error response from the server.
type Error struct {
// Code is the HTTP response status code.
Code int
// Message is the server response message.
Message string
}
func (e *Error) Error() string {
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
}

View File

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

View File

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

View File

@@ -1,292 +0,0 @@
# Azure Active Directory authentication for Go
This is a standalone package for authenticating with Azure Active
Directory from other Go libraries and applications, in particular the [Azure SDK
for Go](https://github.com/Azure/azure-sdk-for-go).
Note: Despite the package's name it is not related to other "ADAL" libraries
maintained in the [github.com/AzureAD](https://github.com/AzureAD) org. Issues
should be opened in [this repo's](https://github.com/Azure/go-autorest/issues)
or [the SDK's](https://github.com/Azure/azure-sdk-for-go/issues) issue
trackers.
## Install
```bash
go get -u github.com/Azure/go-autorest/autorest/adal
```
## Usage
An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) by following these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli).
### Register an Azure AD Application with secret
1. Register a new application with a `secret` credential
```
az ad app create \
--display-name example-app \
--homepage https://example-app/home \
--identifier-uris https://example-app/app \
--password secret
```
2. Create a service principal using the `Application ID` from previous step
```
az ad sp create --id "Application ID"
```
* Replace `Application ID` with `appId` from step 1.
### Register an Azure AD Application with certificate
1. Create a private key
```
openssl genrsa -out "example-app.key" 2048
```
2. Create the certificate
```
openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr"
openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000
```
3. Create the PKCS12 version of the certificate containing also the private key
```
openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass:
```
4. Register a new application with the certificate content form `example-app.crt`
```
certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)"
az ad app create \
--display-name example-app \
--homepage https://example-app/home \
--identifier-uris https://example-app/app \
--key-usage Verify --end-date 2018-01-01 \
--key-value "${certificateContents}"
```
5. Create a service principal using the `Application ID` from previous step
```
az ad sp create --id "APPLICATION_ID"
```
* Replace `APPLICATION_ID` with `appId` from step 4.
### Grant the necessary permissions
Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained
level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles)
which can be assigned to a service principal of an Azure AD application depending of your needs.
```
az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME"
```
* Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step.
* Replace the `ROLE_NAME` with a role name of your choice.
It is also possible to define custom role definitions.
```
az role definition create --role-definition role-definition.json
```
* Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file.
### Acquire Access Token
The common configuration used by all flows:
```Go
const activeDirectoryEndpoint = "https://login.microsoftonline.com/"
tenantID := "TENANT_ID"
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID)
applicationID := "APPLICATION_ID"
callback := func(token adal.Token) error {
// This is called after the token is acquired
}
// The resource for which the token is acquired
resource := "https://management.core.windows.net/"
```
* Replace the `TENANT_ID` with your tenant ID.
* Replace the `APPLICATION_ID` with the value from previous section.
#### Client Credentials
```Go
applicationSecret := "APPLICATION_SECRET"
spt, err := adal.NewServicePrincipalToken(
*oauthConfig,
appliationID,
applicationSecret,
resource,
callbacks...)
if err != nil {
return nil, err
}
// Acquire a new access token
err = spt.Refresh()
if (err == nil) {
token := spt.Token
}
```
* Replace the `APPLICATION_SECRET` with the `password` value from previous section.
#### Client Certificate
```Go
certificatePath := "./example-app.pfx"
certData, err := ioutil.ReadFile(certificatePath)
if err != nil {
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
}
// Get the certificate and private key from pfx file
certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
if err != nil {
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
}
spt, err := adal.NewServicePrincipalTokenFromCertificate(
*oauthConfig,
applicationID,
certificate,
rsaPrivateKey,
resource,
callbacks...)
// Acquire a new access token
err = spt.Refresh()
if (err == nil) {
token := spt.Token
}
```
* Update the certificate path to point to the example-app.pfx file which was created in previous section.
#### Device Code
```Go
oauthClient := &http.Client{}
// Acquire the device code
deviceCode, err := adal.InitiateDeviceAuth(
oauthClient,
*oauthConfig,
applicationID,
resource)
if err != nil {
return nil, fmt.Errorf("Failed to start device auth flow: %s", err)
}
// Display the authentication message
fmt.Println(*deviceCode.Message)
// Wait here until the user is authenticated
token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
if err != nil {
return nil, fmt.Errorf("Failed to finish device auth flow: %s", err)
}
spt, err := adal.NewServicePrincipalTokenFromManualToken(
*oauthConfig,
applicationID,
resource,
*token,
callbacks...)
if (err == nil) {
token := spt.Token
}
```
#### Username password authenticate
```Go
spt, err := adal.NewServicePrincipalTokenFromUsernamePassword(
*oauthConfig,
applicationID,
username,
password,
resource,
callbacks...)
if (err == nil) {
token := spt.Token
}
```
#### Authorization code authenticate
``` Go
spt, err := adal.NewServicePrincipalTokenFromAuthorizationCode(
*oauthConfig,
applicationID,
clientSecret,
authorizationCode,
redirectURI,
resource,
callbacks...)
err = spt.Refresh()
if (err == nil) {
token := spt.Token
}
```
### Command Line Tool
A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above.
```
adal -h
Usage of ./adal:
-applicationId string
application id
-certificatePath string
path to pk12/PFC application certificate
-mode string
authentication mode (device, secret, cert, refresh) (default "device")
-resource string
resource for which the token is requested
-secret string
application secret
-tenantId string
tenant id
-tokenCachePath string
location of oath token cache (default "/home/cgc/.adal/accessToken.json")
```
Example acquire a token for `https://management.core.windows.net/` using device code flow:
```
adal -mode device \
-applicationId "APPLICATION_ID" \
-tenantId "TENANT_ID" \
-resource https://management.core.windows.net/
```

View File

@@ -1,151 +0,0 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"errors"
"fmt"
"net/url"
)
const (
activeDirectoryEndpointTemplate = "%s/oauth2/%s%s"
)
// OAuthConfig represents the endpoints needed
// in OAuth operations
type OAuthConfig struct {
AuthorityEndpoint url.URL `json:"authorityEndpoint"`
AuthorizeEndpoint url.URL `json:"authorizeEndpoint"`
TokenEndpoint url.URL `json:"tokenEndpoint"`
DeviceCodeEndpoint url.URL `json:"deviceCodeEndpoint"`
}
// IsZero returns true if the OAuthConfig object is zero-initialized.
func (oac OAuthConfig) IsZero() bool {
return oac == OAuthConfig{}
}
func validateStringParam(param, name string) error {
if len(param) == 0 {
return fmt.Errorf("parameter '" + name + "' cannot be empty")
}
return nil
}
// NewOAuthConfig returns an OAuthConfig with tenant specific urls
func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
apiVer := "1.0"
return NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID, &apiVer)
}
// NewOAuthConfigWithAPIVersion returns an OAuthConfig with tenant specific urls.
// If apiVersion is not nil the "api-version" query parameter will be appended to the endpoint URLs with the specified value.
func NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID string, apiVersion *string) (*OAuthConfig, error) {
if err := validateStringParam(activeDirectoryEndpoint, "activeDirectoryEndpoint"); err != nil {
return nil, err
}
api := ""
// it's legal for tenantID to be empty so don't validate it
if apiVersion != nil {
if err := validateStringParam(*apiVersion, "apiVersion"); err != nil {
return nil, err
}
api = fmt.Sprintf("?api-version=%s", *apiVersion)
}
u, err := url.Parse(activeDirectoryEndpoint)
if err != nil {
return nil, err
}
authorityURL, err := u.Parse(tenantID)
if err != nil {
return nil, err
}
authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", api))
if err != nil {
return nil, err
}
tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", api))
if err != nil {
return nil, err
}
deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", api))
if err != nil {
return nil, err
}
return &OAuthConfig{
AuthorityEndpoint: *authorityURL,
AuthorizeEndpoint: *authorizeURL,
TokenEndpoint: *tokenURL,
DeviceCodeEndpoint: *deviceCodeURL,
}, nil
}
// MultiTenantOAuthConfig provides endpoints for primary and aulixiary tenant IDs.
type MultiTenantOAuthConfig interface {
PrimaryTenant() *OAuthConfig
AuxiliaryTenants() []*OAuthConfig
}
// OAuthOptions contains optional OAuthConfig creation arguments.
type OAuthOptions struct {
APIVersion string
}
func (c OAuthOptions) apiVersion() string {
if c.APIVersion != "" {
return fmt.Sprintf("?api-version=%s", c.APIVersion)
}
return "1.0"
}
// NewMultiTenantOAuthConfig creates an object that support multitenant OAuth configuration.
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/authenticate-multi-tenant for more information.
func NewMultiTenantOAuthConfig(activeDirectoryEndpoint, primaryTenantID string, auxiliaryTenantIDs []string, options OAuthOptions) (MultiTenantOAuthConfig, error) {
if len(auxiliaryTenantIDs) == 0 || len(auxiliaryTenantIDs) > 3 {
return nil, errors.New("must specify one to three auxiliary tenants")
}
mtCfg := multiTenantOAuthConfig{
cfgs: make([]*OAuthConfig, len(auxiliaryTenantIDs)+1),
}
apiVer := options.apiVersion()
pri, err := NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, primaryTenantID, &apiVer)
if err != nil {
return nil, fmt.Errorf("failed to create OAuthConfig for primary tenant: %v", err)
}
mtCfg.cfgs[0] = pri
for i := range auxiliaryTenantIDs {
aux, err := NewOAuthConfig(activeDirectoryEndpoint, auxiliaryTenantIDs[i])
if err != nil {
return nil, fmt.Errorf("failed to create OAuthConfig for tenant '%s': %v", auxiliaryTenantIDs[i], err)
}
mtCfg.cfgs[i+1] = aux
}
return mtCfg, nil
}
type multiTenantOAuthConfig struct {
// first config in the slice is the primary tenant
cfgs []*OAuthConfig
}
func (m multiTenantOAuthConfig) PrimaryTenant() *OAuthConfig {
return m.cfgs[0]
}
func (m multiTenantOAuthConfig) AuxiliaryTenants() []*OAuthConfig {
return m.cfgs[1:]
}

View File

@@ -1,242 +0,0 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
This file is largely based on rjw57/oauth2device's code, with the follow differences:
* scope -> resource, and only allow a single one
* receive "Message" in the DeviceCode struct and show it to users as the prompt
* azure-xplat-cli has the following behavior that this emulates:
- does not send client_secret during the token exchange
- sends resource again in the token exchange request
*/
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
const (
logPrefix = "autorest/adal/devicetoken:"
)
var (
// ErrDeviceGeneric represents an unknown error from the token endpoint when using device flow
ErrDeviceGeneric = fmt.Errorf("%s Error while retrieving OAuth token: Unknown Error", logPrefix)
// ErrDeviceAccessDenied represents an access denied error from the token endpoint when using device flow
ErrDeviceAccessDenied = fmt.Errorf("%s Error while retrieving OAuth token: Access Denied", logPrefix)
// ErrDeviceAuthorizationPending represents the server waiting on the user to complete the device flow
ErrDeviceAuthorizationPending = fmt.Errorf("%s Error while retrieving OAuth token: Authorization Pending", logPrefix)
// ErrDeviceCodeExpired represents the server timing out and expiring the code during device flow
ErrDeviceCodeExpired = fmt.Errorf("%s Error while retrieving OAuth token: Code Expired", logPrefix)
// ErrDeviceSlowDown represents the service telling us we're polling too often during device flow
ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix)
// ErrDeviceCodeEmpty represents an empty device code from the device endpoint while using device flow
ErrDeviceCodeEmpty = fmt.Errorf("%s Error while retrieving device code: Device Code Empty", logPrefix)
// ErrOAuthTokenEmpty represents an empty OAuth token from the token endpoint when using device flow
ErrOAuthTokenEmpty = fmt.Errorf("%s Error while retrieving OAuth token: Token Empty", logPrefix)
errCodeSendingFails = "Error occurred while sending request for Device Authorization Code"
errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint"
errTokenSendingFails = "Error occurred while sending request with device code for a token"
errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)"
errStatusNotOK = "Error HTTP status != 200"
)
// DeviceCode is the object returned by the device auth endpoint
// It contains information to instruct the user to complete the auth flow
type DeviceCode struct {
DeviceCode *string `json:"device_code,omitempty"`
UserCode *string `json:"user_code,omitempty"`
VerificationURL *string `json:"verification_url,omitempty"`
ExpiresIn *int64 `json:"expires_in,string,omitempty"`
Interval *int64 `json:"interval,string,omitempty"`
Message *string `json:"message"` // Azure specific
Resource string // store the following, stored when initiating, used when exchanging
OAuthConfig OAuthConfig
ClientID string
}
// TokenError is the object returned by the token exchange endpoint
// when something is amiss
type TokenError struct {
Error *string `json:"error,omitempty"`
ErrorCodes []int `json:"error_codes,omitempty"`
ErrorDescription *string `json:"error_description,omitempty"`
Timestamp *string `json:"timestamp,omitempty"`
TraceID *string `json:"trace_id,omitempty"`
}
// DeviceToken is the object return by the token exchange endpoint
// It can either look like a Token or an ErrorToken, so put both here
// and check for presence of "Error" to know if we are in error state
type deviceToken struct {
Token
TokenError
}
// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode
// that can be used with CheckForUserCompletion or WaitForUserCompletion.
func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
v := url.Values{
"client_id": []string{clientID},
"resource": []string{resource},
}
s := v.Encode()
body := ioutil.NopCloser(strings.NewReader(s))
req, err := http.NewRequest(http.MethodPost, oauthConfig.DeviceCodeEndpoint.String(), body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
}
req.ContentLength = int64(len(s))
req.Header.Set(contentType, mimeTypeFormPost)
resp, err := sender.Do(req)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
}
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, errStatusNotOK)
}
if len(strings.Trim(string(rb), " ")) == 0 {
return nil, ErrDeviceCodeEmpty
}
var code DeviceCode
err = json.Unmarshal(rb, &code)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
}
code.ClientID = clientID
code.Resource = resource
code.OAuthConfig = oauthConfig
return &code, nil
}
// CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint
// to see if the device flow has: been completed, timed out, or otherwise failed
func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
v := url.Values{
"client_id": []string{code.ClientID},
"code": []string{*code.DeviceCode},
"grant_type": []string{OAuthGrantTypeDeviceCode},
"resource": []string{code.Resource},
}
s := v.Encode()
body := ioutil.NopCloser(strings.NewReader(s))
req, err := http.NewRequest(http.MethodPost, code.OAuthConfig.TokenEndpoint.String(), body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
}
req.ContentLength = int64(len(s))
req.Header.Set(contentType, mimeTypeFormPost)
resp, err := sender.Do(req)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
}
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
}
if resp.StatusCode != http.StatusOK && len(strings.Trim(string(rb), " ")) == 0 {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, errStatusNotOK)
}
if len(strings.Trim(string(rb), " ")) == 0 {
return nil, ErrOAuthTokenEmpty
}
var token deviceToken
err = json.Unmarshal(rb, &token)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
}
if token.Error == nil {
return &token.Token, nil
}
switch *token.Error {
case "authorization_pending":
return nil, ErrDeviceAuthorizationPending
case "slow_down":
return nil, ErrDeviceSlowDown
case "access_denied":
return nil, ErrDeviceAccessDenied
case "code_expired":
return nil, ErrDeviceCodeExpired
default:
return nil, ErrDeviceGeneric
}
}
// WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs.
// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'.
func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
intervalDuration := time.Duration(*code.Interval) * time.Second
waitDuration := intervalDuration
for {
token, err := CheckForUserCompletion(sender, code)
if err == nil {
return token, nil
}
switch err {
case ErrDeviceSlowDown:
waitDuration += waitDuration
case ErrDeviceAuthorizationPending:
// noop
default: // everything else is "fatal" to us
return nil, err
}
if waitDuration > (intervalDuration * 3) {
return nil, fmt.Errorf("%s Error waiting for user to complete device flow. Server told us to slow_down too much", logPrefix)
}
time.Sleep(waitDuration)
}
}

View File

@@ -1,11 +0,0 @@
module github.com/Azure/go-autorest/autorest/adal
go 1.12
require (
github.com/Azure/go-autorest/autorest/date v0.1.0
github.com/Azure/go-autorest/autorest/mocks v0.1.0
github.com/Azure/go-autorest/tracing v0.5.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
)

View File

@@ -1,12 +0,0 @@
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -1,73 +0,0 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
// LoadToken restores a Token object from a file located at 'path'.
func LoadToken(path string) (*Token, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err)
}
defer file.Close()
var token Token
dec := json.NewDecoder(file)
if err = dec.Decode(&token); err != nil {
return nil, fmt.Errorf("failed to decode contents of file (%s) into Token representation: %v", path, err)
}
return &token, nil
}
// SaveToken persists an oauth token at the given location on disk.
// It moves the new file into place so it can safely be used to replace an existing file
// that maybe accessed by multiple processes.
func SaveToken(path string, mode os.FileMode, token Token) error {
dir := filepath.Dir(path)
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory (%s) to store token in: %v", dir, err)
}
newFile, err := ioutil.TempFile(dir, "token")
if err != nil {
return fmt.Errorf("failed to create the temp file to write the token: %v", err)
}
tempPath := newFile.Name()
if err := json.NewEncoder(newFile).Encode(token); err != nil {
return fmt.Errorf("failed to encode token to file (%s) while saving token: %v", tempPath, err)
}
if err := newFile.Close(); err != nil {
return fmt.Errorf("failed to close temp file %s: %v", tempPath, err)
}
// Atomic replace to avoid multi-writer file corruptions
if err := os.Rename(tempPath, path); err != nil {
return fmt.Errorf("failed to move temporary token to desired output location. src=%s dst=%s: %v", tempPath, path, err)
}
if err := os.Chmod(path, mode); err != nil {
return fmt.Errorf("failed to chmod the token file %s: %v", path, err)
}
return nil
}

View File

@@ -1,95 +0,0 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"crypto/tls"
"net/http"
"net/http/cookiejar"
"sync"
"github.com/Azure/go-autorest/tracing"
)
const (
contentType = "Content-Type"
mimeTypeFormPost = "application/x-www-form-urlencoded"
)
var defaultSender Sender
var defaultSenderInit = &sync.Once{}
// Sender is the interface that wraps the Do method to send HTTP requests.
//
// The standard http.Client conforms to this interface.
type Sender interface {
Do(*http.Request) (*http.Response, error)
}
// SenderFunc is a method that implements the Sender interface.
type SenderFunc func(*http.Request) (*http.Response, error)
// Do implements the Sender interface on SenderFunc.
func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
return sf(r)
}
// SendDecorator takes and possibly decorates, by wrapping, a Sender. Decorators may affect the
// http.Request and pass it along or, first, pass the http.Request along then react to the
// http.Response result.
type SendDecorator func(Sender) Sender
// CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
func CreateSender(decorators ...SendDecorator) Sender {
return DecorateSender(sender(), decorators...)
}
// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
// the Sender. Decorators are applied in the order received, but their affect upon the request
// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
// post-decorator (pass the http.Request along and react to the results in http.Response).
func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
for _, decorate := range decorators {
s = decorate(s)
}
return s
}
func sender() Sender {
// note that we can't init defaultSender in init() since it will
// execute before calling code has had a chance to enable tracing
defaultSenderInit.Do(func() {
// Use behaviour compatible with DefaultTransport, but require TLS minimum version.
defaultTransport := http.DefaultTransport.(*http.Transport)
transport := &http.Transport{
Proxy: defaultTransport.Proxy,
DialContext: defaultTransport.DialContext,
MaxIdleConns: defaultTransport.MaxIdleConns,
IdleConnTimeout: defaultTransport.IdleConnTimeout,
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
}
var roundTripper http.RoundTripper = transport
if tracing.IsEnabled() {
roundTripper = tracing.NewTransport(transport)
}
j, _ := cookiejar.New(nil)
defaultSender = &http.Client{Jar: j, Transport: roundTripper}
})
return defaultSender
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,45 +0,0 @@
package adal
import (
"fmt"
"runtime"
)
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const number = "v1.0.0"
var (
ua = fmt.Sprintf("Go/%s (%s-%s) go-autorest/adal/%s",
runtime.Version(),
runtime.GOARCH,
runtime.GOOS,
number,
)
)
// UserAgent returns a string containing the Go version, system architecture and OS, and the adal version.
func UserAgent() string {
return ua
}
// AddToUserAgent adds an extension to the current user agent
func AddToUserAgent(extension string) error {
if extension != "" {
ua = fmt.Sprintf("%s %s", ua, extension)
return nil
}
return fmt.Errorf("Extension was empty, User Agent remained as '%s'", ua)
}

View File

@@ -1,336 +0,0 @@
package autorest
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"crypto/tls"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/Azure/go-autorest/autorest/adal"
)
const (
bearerChallengeHeader = "Www-Authenticate"
bearer = "Bearer"
tenantID = "tenantID"
apiKeyAuthorizerHeader = "Ocp-Apim-Subscription-Key"
bingAPISdkHeader = "X-BingApis-SDK-Client"
golangBingAPISdkHeaderValue = "Go-SDK"
authorization = "Authorization"
basic = "Basic"
)
// Authorizer is the interface that provides a PrepareDecorator used to supply request
// authorization. Most often, the Authorizer decorator runs last so it has access to the full
// state of the formed HTTP request.
type Authorizer interface {
WithAuthorization() PrepareDecorator
}
// NullAuthorizer implements a default, "do nothing" Authorizer.
type NullAuthorizer struct{}
// WithAuthorization returns a PrepareDecorator that does nothing.
func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
return WithNothing()
}
// APIKeyAuthorizer implements API Key authorization.
type APIKeyAuthorizer struct {
headers map[string]interface{}
queryParameters map[string]interface{}
}
// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers.
func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer {
return NewAPIKeyAuthorizer(headers, nil)
}
// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters.
func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer {
return NewAPIKeyAuthorizer(nil, queryParameters)
}
// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers.
func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer {
return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Parameters.
func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters))
}
}
// CognitiveServicesAuthorizer implements authorization for Cognitive Services.
type CognitiveServicesAuthorizer struct {
subscriptionKey string
}
// NewCognitiveServicesAuthorizer is
func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer {
return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey}
}
// WithAuthorization is
func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator {
headers := make(map[string]interface{})
headers[apiKeyAuthorizerHeader] = csa.subscriptionKey
headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue
return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
}
// BearerAuthorizer implements the bearer authorization
type BearerAuthorizer struct {
tokenProvider adal.OAuthTokenProvider
}
// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
return &BearerAuthorizer{tokenProvider: tp}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
// value is "Bearer " followed by the token.
//
// By default, the token will be automatically refreshed through the Refresher interface.
func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err == nil {
// the ordering is important here, prefer RefresherWithContext if available
if refresher, ok := ba.tokenProvider.(adal.RefresherWithContext); ok {
err = refresher.EnsureFreshWithContext(r.Context())
} else if refresher, ok := ba.tokenProvider.(adal.Refresher); ok {
err = refresher.EnsureFresh()
}
if err != nil {
var resp *http.Response
if tokError, ok := err.(adal.TokenRefreshError); ok {
resp = tokError.Response()
}
return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp,
"Failed to refresh the Token for request to %s", r.URL)
}
return Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken())))
}
return r, err
})
}
}
// BearerAuthorizerCallbackFunc is the authentication callback signature.
type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
// BearerAuthorizerCallback implements bearer authorization via a callback.
type BearerAuthorizerCallback struct {
sender Sender
callback BearerAuthorizerCallbackFunc
}
// NewBearerAuthorizerCallback creates a bearer authorization callback. The callback
// is invoked when the HTTP request is submitted.
func NewBearerAuthorizerCallback(s Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
if s == nil {
s = sender(tls.RenegotiateNever)
}
return &BearerAuthorizerCallback{sender: s, callback: callback}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
// is "Bearer " followed by the token. The BearerAuthorizer is obtained via a user-supplied callback.
//
// By default, the token will be automatically refreshed through the Refresher interface.
func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err == nil {
// make a copy of the request and remove the body as it's not
// required and avoids us having to create a copy of it.
rCopy := *r
removeRequestBody(&rCopy)
resp, err := bacb.sender.Do(&rCopy)
if err == nil && resp.StatusCode == 401 {
defer resp.Body.Close()
if hasBearerChallenge(resp) {
bc, err := newBearerChallenge(resp)
if err != nil {
return r, err
}
if bacb.callback != nil {
ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
if err != nil {
return r, err
}
return Prepare(r, ba.WithAuthorization())
}
}
}
}
return r, err
})
}
}
// returns true if the HTTP response contains a bearer challenge
func hasBearerChallenge(resp *http.Response) bool {
authHeader := resp.Header.Get(bearerChallengeHeader)
if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
return false
}
return true
}
type bearerChallenge struct {
values map[string]string
}
func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) {
challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader))
trimmedChallenge := challenge[len(bearer)+1:]
// challenge is a set of key=value pairs that are comma delimited
pairs := strings.Split(trimmedChallenge, ",")
if len(pairs) < 1 {
err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
return bc, err
}
bc.values = make(map[string]string)
for i := range pairs {
trimmedPair := strings.TrimSpace(pairs[i])
pair := strings.Split(trimmedPair, "=")
if len(pair) == 2 {
// remove the enclosing quotes
key := strings.Trim(pair[0], "\"")
value := strings.Trim(pair[1], "\"")
switch key {
case "authorization", "authorization_uri":
// strip the tenant ID from the authorization URL
asURL, err := url.Parse(value)
if err != nil {
return bc, err
}
bc.values[tenantID] = asURL.Path[1:]
default:
bc.values[key] = value
}
}
}
return bc, err
}
// EventGridKeyAuthorizer implements authorization for event grid using key authentication.
type EventGridKeyAuthorizer struct {
topicKey string
}
// NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer
// with the specified topic key.
func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer {
return EventGridKeyAuthorizer{topicKey: topicKey}
}
// WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header.
func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator {
headers := map[string]interface{}{
"aeg-sas-key": egta.topicKey,
}
return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
}
// BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header
// with the value "Basic <TOKEN>" where <TOKEN> is a base64-encoded username:password tuple.
type BasicAuthorizer struct {
userName string
password string
}
// NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password.
func NewBasicAuthorizer(userName, password string) *BasicAuthorizer {
return &BasicAuthorizer{
userName: userName,
password: password,
}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
// value is "Basic " followed by the base64-encoded username:password tuple.
func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator {
headers := make(map[string]interface{})
headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password)))
return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
}
// MultiTenantServicePrincipalTokenAuthorizer provides authentication across tenants.
type MultiTenantServicePrincipalTokenAuthorizer interface {
WithAuthorization() PrepareDecorator
}
// NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider
func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer {
return &multiTenantSPTAuthorizer{tp: tp}
}
type multiTenantSPTAuthorizer struct {
tp adal.MultitenantOAuthTokenProvider
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the
// primary token along with the auxiliary authorization header using the auxiliary tokens.
//
// By default, the token will be automatically refreshed through the Refresher interface.
func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err != nil {
return r, err
}
if refresher, ok := mt.tp.(adal.RefresherWithContext); ok {
err = refresher.EnsureFreshWithContext(r.Context())
if err != nil {
var resp *http.Response
if tokError, ok := err.(adal.TokenRefreshError); ok {
resp = tokError.Response()
}
return r, NewErrorWithError(err, "azure.multiTenantSPTAuthorizer", "WithAuthorization", resp,
"Failed to refresh one or more Tokens for request to %s", r.URL)
}
}
r, err = Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", mt.tp.PrimaryOAuthToken())))
if err != nil {
return r, err
}
auxTokens := mt.tp.AuxiliaryOAuthTokens()
for i := range auxTokens {
auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
}
return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, "; ")))
})
}
}

View File

@@ -1,150 +0,0 @@
/*
Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines
and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/)
generated Go code.
The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
and Responding. A typical pattern is:
req, err := Prepare(&http.Request{},
token.WithAuthorization())
resp, err := Send(req,
WithLogging(logger),
DoErrorIfStatusCode(http.StatusInternalServerError),
DoCloseIfError(),
DoRetryForAttempts(5, time.Second))
err = Respond(resp,
ByDiscardingBody(),
ByClosing())
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
and then pass the data along, pass the data first and then modify the result, or wrap themselves
around passing the data (such as a logger might do). Decorators run in the order provided. For
example, the following:
req, err := Prepare(&http.Request{},
WithBaseURL("https://microsoft.com/"),
WithPath("a"),
WithPath("b"),
WithPath("c"))
will set the URL to:
https://microsoft.com/a/b/c
Preparers and Responders may be shared and re-used (assuming the underlying decorators support
sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
all bound together by means of input / output channels.
Decorators hold their passed state within a closure (such as the path components in the example
above). Be careful to share Preparers and Responders only in a context where such held state
applies. For example, it may not make sense to share a Preparer that applies a query string from a
fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
struct (e.g., ByUnmarshallingJson) is likely incorrect.
Lastly, the Swagger specification (https://swagger.io) that drives AutoRest
(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure
correct parsing and formatting.
Errors raised by autorest objects and methods will conform to the autorest.Error interface.
See the included examples for more detail. For details on the suggested use of this package by
generated clients, see the Client described below.
*/
package autorest
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"context"
"net/http"
"time"
)
const (
// HeaderLocation specifies the HTTP Location header.
HeaderLocation = "Location"
// HeaderRetryAfter specifies the HTTP Retry-After header.
HeaderRetryAfter = "Retry-After"
)
// ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set
// and false otherwise.
func ResponseHasStatusCode(resp *http.Response, codes ...int) bool {
if resp == nil {
return false
}
return containsInt(codes, resp.StatusCode)
}
// GetLocation retrieves the URL from the Location header of the passed response.
func GetLocation(resp *http.Response) string {
return resp.Header.Get(HeaderLocation)
}
// GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If
// the header is absent or is malformed, it will return the supplied default delay time.Duration.
func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration {
retry := resp.Header.Get(HeaderRetryAfter)
if retry == "" {
return defaultDelay
}
d, err := time.ParseDuration(retry + "s")
if err != nil {
return defaultDelay
}
return d
}
// NewPollingRequest allocates and returns a new http.Request to poll for the passed response.
func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) {
location := GetLocation(resp)
if location == "" {
return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling")
}
req, err := Prepare(&http.Request{Cancel: cancel},
AsGet(),
WithBaseURL(location))
if err != nil {
return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location)
}
return req, nil
}
// NewPollingRequestWithContext allocates and returns a new http.Request with the specified context to poll for the passed response.
func NewPollingRequestWithContext(ctx context.Context, resp *http.Response) (*http.Request, error) {
location := GetLocation(resp)
if location == "" {
return nil, NewErrorWithResponse("autorest", "NewPollingRequestWithContext", resp, "Location header missing from response that requires polling")
}
req, err := Prepare((&http.Request{}).WithContext(ctx),
AsGet(),
WithBaseURL(location))
if err != nil {
return nil, NewErrorWithError(err, "autorest", "NewPollingRequestWithContext", nil, "Failure creating poll request to %s", location)
}
return req, nil
}

View File

@@ -1,924 +0,0 @@
package azure
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/tracing"
)
const (
headerAsyncOperation = "Azure-AsyncOperation"
)
const (
operationInProgress string = "InProgress"
operationCanceled string = "Canceled"
operationFailed string = "Failed"
operationSucceeded string = "Succeeded"
)
var pollingCodes = [...]int{http.StatusNoContent, http.StatusAccepted, http.StatusCreated, http.StatusOK}
// Future provides a mechanism to access the status and results of an asynchronous request.
// Since futures are stateful they should be passed by value to avoid race conditions.
type Future struct {
pt pollingTracker
}
// NewFutureFromResponse returns a new Future object initialized
// with the initial response from an asynchronous operation.
func NewFutureFromResponse(resp *http.Response) (Future, error) {
pt, err := createPollingTracker(resp)
return Future{pt: pt}, err
}
// Response returns the last HTTP response.
func (f Future) Response() *http.Response {
if f.pt == nil {
return nil
}
return f.pt.latestResponse()
}
// Status returns the last status message of the operation.
func (f Future) Status() string {
if f.pt == nil {
return ""
}
return f.pt.pollingStatus()
}
// PollingMethod returns the method used to monitor the status of the asynchronous operation.
func (f Future) PollingMethod() PollingMethodType {
if f.pt == nil {
return PollingUnknown
}
return f.pt.pollingMethod()
}
// DoneWithContext queries the service to see if the operation has completed.
func (f *Future) DoneWithContext(ctx context.Context, sender autorest.Sender) (done bool, err error) {
ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.DoneWithContext")
defer func() {
sc := -1
resp := f.Response()
if resp != nil {
sc = resp.StatusCode
}
tracing.EndSpan(ctx, sc, err)
}()
if f.pt == nil {
return false, autorest.NewError("Future", "Done", "future is not initialized")
}
if f.pt.hasTerminated() {
return true, f.pt.pollingError()
}
if err := f.pt.pollForStatus(ctx, sender); err != nil {
return false, err
}
if err := f.pt.checkForErrors(); err != nil {
return f.pt.hasTerminated(), err
}
if err := f.pt.updatePollingState(f.pt.provisioningStateApplicable()); err != nil {
return false, err
}
if err := f.pt.initPollingMethod(); err != nil {
return false, err
}
if err := f.pt.updatePollingMethod(); err != nil {
return false, err
}
return f.pt.hasTerminated(), f.pt.pollingError()
}
// GetPollingDelay returns a duration the application should wait before checking
// the status of the asynchronous request and true; this value is returned from
// the service via the Retry-After response header. If the header wasn't returned
// then the function returns the zero-value time.Duration and false.
func (f Future) GetPollingDelay() (time.Duration, bool) {
if f.pt == nil {
return 0, false
}
resp := f.pt.latestResponse()
if resp == nil {
return 0, false
}
retry := resp.Header.Get(autorest.HeaderRetryAfter)
if retry == "" {
return 0, false
}
d, err := time.ParseDuration(retry + "s")
if err != nil {
panic(err)
}
return d, true
}
// WaitForCompletionRef will return when one of the following conditions is met: the long
// running operation has completed, the provided context is cancelled, or the client's
// polling duration has been exceeded. It will retry failed polling attempts based on
// the retry value defined in the client up to the maximum retry attempts.
// If no deadline is specified in the context then the client.PollingDuration will be
// used to determine if a default deadline should be used.
// If PollingDuration is greater than zero the value will be used as the context's timeout.
// If PollingDuration is zero then no default deadline will be used.
func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Client) (err error) {
ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.WaitForCompletionRef")
defer func() {
sc := -1
resp := f.Response()
if resp != nil {
sc = resp.StatusCode
}
tracing.EndSpan(ctx, sc, err)
}()
cancelCtx := ctx
// if the provided context already has a deadline don't override it
_, hasDeadline := ctx.Deadline()
if d := client.PollingDuration; !hasDeadline && d != 0 {
var cancel context.CancelFunc
cancelCtx, cancel = context.WithTimeout(ctx, d)
defer cancel()
}
done, err := f.DoneWithContext(ctx, client)
for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) {
if attempts >= client.RetryAttempts {
return autorest.NewErrorWithError(err, "Future", "WaitForCompletion", f.pt.latestResponse(), "the number of retries has been exceeded")
}
// we want delayAttempt to be zero in the non-error case so
// that DelayForBackoff doesn't perform exponential back-off
var delayAttempt int
var delay time.Duration
if err == nil {
// check for Retry-After delay, if not present use the client's polling delay
var ok bool
delay, ok = f.GetPollingDelay()
if !ok {
delay = client.PollingDelay
}
} else {
// there was an error polling for status so perform exponential
// back-off based on the number of attempts using the client's retry
// duration. update attempts after delayAttempt to avoid off-by-one.
delayAttempt = attempts
delay = client.RetryDuration
attempts++
}
// wait until the delay elapses or the context is cancelled
delayElapsed := autorest.DelayForBackoff(delay, delayAttempt, cancelCtx.Done())
if !delayElapsed {
return autorest.NewErrorWithError(cancelCtx.Err(), "Future", "WaitForCompletion", f.pt.latestResponse(), "context has been cancelled")
}
}
return
}
// MarshalJSON implements the json.Marshaler interface.
func (f Future) MarshalJSON() ([]byte, error) {
return json.Marshal(f.pt)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (f *Future) UnmarshalJSON(data []byte) error {
// unmarshal into JSON object to determine the tracker type
obj := map[string]interface{}{}
err := json.Unmarshal(data, &obj)
if err != nil {
return err
}
if obj["method"] == nil {
return autorest.NewError("Future", "UnmarshalJSON", "missing 'method' property")
}
method := obj["method"].(string)
switch strings.ToUpper(method) {
case http.MethodDelete:
f.pt = &pollingTrackerDelete{}
case http.MethodPatch:
f.pt = &pollingTrackerPatch{}
case http.MethodPost:
f.pt = &pollingTrackerPost{}
case http.MethodPut:
f.pt = &pollingTrackerPut{}
default:
return autorest.NewError("Future", "UnmarshalJSON", "unsupoorted method '%s'", method)
}
// now unmarshal into the tracker
return json.Unmarshal(data, &f.pt)
}
// PollingURL returns the URL used for retrieving the status of the long-running operation.
func (f Future) PollingURL() string {
if f.pt == nil {
return ""
}
return f.pt.pollingURL()
}
// GetResult should be called once polling has completed successfully.
// It makes the final GET call to retrieve the resultant payload.
func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) {
if f.pt.finalGetURL() == "" {
// we can end up in this situation if the async operation returns a 200
// with no polling URLs. in that case return the response which should
// contain the JSON payload (only do this for successful terminal cases).
if lr := f.pt.latestResponse(); lr != nil && f.pt.hasSucceeded() {
return lr, nil
}
return nil, autorest.NewError("Future", "GetResult", "missing URL for retrieving result")
}
req, err := http.NewRequest(http.MethodGet, f.pt.finalGetURL(), nil)
if err != nil {
return nil, err
}
return sender.Do(req)
}
type pollingTracker interface {
// these methods can differ per tracker
// checks the response headers and status code to determine the polling mechanism
updatePollingMethod() error
// checks the response for tracker-specific error conditions
checkForErrors() error
// returns true if provisioning state should be checked
provisioningStateApplicable() bool
// methods common to all trackers
// initializes a tracker's polling URL and method, called for each iteration.
// these values can be overridden by each polling tracker as required.
initPollingMethod() error
// initializes the tracker's internal state, call this when the tracker is created
initializeState() error
// makes an HTTP request to check the status of the LRO
pollForStatus(ctx context.Context, sender autorest.Sender) error
// updates internal tracker state, call this after each call to pollForStatus
updatePollingState(provStateApl bool) error
// returns the error response from the service, can be nil
pollingError() error
// returns the polling method being used
pollingMethod() PollingMethodType
// returns the state of the LRO as returned from the service
pollingStatus() string
// returns the URL used for polling status
pollingURL() string
// returns the URL used for the final GET to retrieve the resource
finalGetURL() string
// returns true if the LRO is in a terminal state
hasTerminated() bool
// returns true if the LRO is in a failed terminal state
hasFailed() bool
// returns true if the LRO is in a successful terminal state
hasSucceeded() bool
// returns the cached HTTP response after a call to pollForStatus(), can be nil
latestResponse() *http.Response
}
type pollingTrackerBase struct {
// resp is the last response, either from the submission of the LRO or from polling
resp *http.Response
// method is the HTTP verb, this is needed for deserialization
Method string `json:"method"`
// rawBody is the raw JSON response body
rawBody map[string]interface{}
// denotes if polling is using async-operation or location header
Pm PollingMethodType `json:"pollingMethod"`
// the URL to poll for status
URI string `json:"pollingURI"`
// the state of the LRO as returned from the service
State string `json:"lroState"`
// the URL to GET for the final result
FinalGetURI string `json:"resultURI"`
// used to hold an error object returned from the service
Err *ServiceError `json:"error,omitempty"`
}
func (pt *pollingTrackerBase) initializeState() error {
// determine the initial polling state based on response body and/or HTTP status
// code. this is applicable to the initial LRO response, not polling responses!
pt.Method = pt.resp.Request.Method
if err := pt.updateRawBody(); err != nil {
return err
}
switch pt.resp.StatusCode {
case http.StatusOK:
if ps := pt.getProvisioningState(); ps != nil {
pt.State = *ps
if pt.hasFailed() {
pt.updateErrorFromResponse()
return pt.pollingError()
}
} else {
pt.State = operationSucceeded
}
case http.StatusCreated:
if ps := pt.getProvisioningState(); ps != nil {
pt.State = *ps
} else {
pt.State = operationInProgress
}
case http.StatusAccepted:
pt.State = operationInProgress
case http.StatusNoContent:
pt.State = operationSucceeded
default:
pt.State = operationFailed
pt.updateErrorFromResponse()
return pt.pollingError()
}
return pt.initPollingMethod()
}
func (pt pollingTrackerBase) getProvisioningState() *string {
if pt.rawBody != nil && pt.rawBody["properties"] != nil {
p := pt.rawBody["properties"].(map[string]interface{})
if ps := p["provisioningState"]; ps != nil {
s := ps.(string)
return &s
}
}
return nil
}
func (pt *pollingTrackerBase) updateRawBody() error {
pt.rawBody = map[string]interface{}{}
if pt.resp.ContentLength != 0 {
defer pt.resp.Body.Close()
b, err := ioutil.ReadAll(pt.resp.Body)
if err != nil {
return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to read response body")
}
// observed in 204 responses over HTTP/2.0; the content length is -1 but body is empty
if len(b) == 0 {
return nil
}
// put the body back so it's available to other callers
pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
if err = json.Unmarshal(b, &pt.rawBody); err != nil {
return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to unmarshal response body")
}
}
return nil
}
func (pt *pollingTrackerBase) pollForStatus(ctx context.Context, sender autorest.Sender) error {
req, err := http.NewRequest(http.MethodGet, pt.URI, nil)
if err != nil {
return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to create HTTP request")
}
req = req.WithContext(ctx)
preparer := autorest.CreatePreparer(autorest.GetPrepareDecorators(ctx)...)
req, err = preparer.Prepare(req)
if err != nil {
return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed preparing HTTP request")
}
pt.resp, err = sender.Do(req)
if err != nil {
return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to send HTTP request")
}
if autorest.ResponseHasStatusCode(pt.resp, pollingCodes[:]...) {
// reset the service error on success case
pt.Err = nil
err = pt.updateRawBody()
} else {
// check response body for error content
pt.updateErrorFromResponse()
err = pt.pollingError()
}
return err
}
// attempts to unmarshal a ServiceError type from the response body.
// if that fails then make a best attempt at creating something meaningful.
// NOTE: this assumes that the async operation has failed.
func (pt *pollingTrackerBase) updateErrorFromResponse() {
var err error
if pt.resp.ContentLength != 0 {
type respErr struct {
ServiceError *ServiceError `json:"error"`
}
re := respErr{}
defer pt.resp.Body.Close()
var b []byte
if b, err = ioutil.ReadAll(pt.resp.Body); err != nil || len(b) == 0 {
goto Default
}
if err = json.Unmarshal(b, &re); err != nil {
goto Default
}
// unmarshalling the error didn't yield anything, try unwrapped error
if re.ServiceError == nil {
err = json.Unmarshal(b, &re.ServiceError)
if err != nil {
goto Default
}
}
// the unmarshaller will ensure re.ServiceError is non-nil
// even if there was no content unmarshalled so check the code.
if re.ServiceError.Code != "" {
pt.Err = re.ServiceError
return
}
}
Default:
se := &ServiceError{
Code: pt.pollingStatus(),
Message: "The async operation failed.",
}
if err != nil {
se.InnerError = make(map[string]interface{})
se.InnerError["unmarshalError"] = err.Error()
}
// stick the response body into the error object in hopes
// it contains something useful to help diagnose the failure.
if len(pt.rawBody) > 0 {
se.AdditionalInfo = []map[string]interface{}{
pt.rawBody,
}
}
pt.Err = se
}
func (pt *pollingTrackerBase) updatePollingState(provStateApl bool) error {
if pt.Pm == PollingAsyncOperation && pt.rawBody["status"] != nil {
pt.State = pt.rawBody["status"].(string)
} else {
if pt.resp.StatusCode == http.StatusAccepted {
pt.State = operationInProgress
} else if provStateApl {
if ps := pt.getProvisioningState(); ps != nil {
pt.State = *ps
} else {
pt.State = operationSucceeded
}
} else {
return autorest.NewError("pollingTrackerBase", "updatePollingState", "the response from the async operation has an invalid status code")
}
}
// if the operation has failed update the error state
if pt.hasFailed() {
pt.updateErrorFromResponse()
}
return nil
}
func (pt pollingTrackerBase) pollingError() error {
if pt.Err == nil {
return nil
}
return pt.Err
}
func (pt pollingTrackerBase) pollingMethod() PollingMethodType {
return pt.Pm
}
func (pt pollingTrackerBase) pollingStatus() string {
return pt.State
}
func (pt pollingTrackerBase) pollingURL() string {
return pt.URI
}
func (pt pollingTrackerBase) finalGetURL() string {
return pt.FinalGetURI
}
func (pt pollingTrackerBase) hasTerminated() bool {
return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed) || strings.EqualFold(pt.State, operationSucceeded)
}
func (pt pollingTrackerBase) hasFailed() bool {
return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed)
}
func (pt pollingTrackerBase) hasSucceeded() bool {
return strings.EqualFold(pt.State, operationSucceeded)
}
func (pt pollingTrackerBase) latestResponse() *http.Response {
return pt.resp
}
// error checking common to all trackers
func (pt pollingTrackerBase) baseCheckForErrors() error {
// for Azure-AsyncOperations the response body cannot be nil or empty
if pt.Pm == PollingAsyncOperation {
if pt.resp.Body == nil || pt.resp.ContentLength == 0 {
return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "for Azure-AsyncOperation response body cannot be nil")
}
if pt.rawBody["status"] == nil {
return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "missing status property in Azure-AsyncOperation response body")
}
}
return nil
}
// default initialization of polling URL/method. each verb tracker will update this as required.
func (pt *pollingTrackerBase) initPollingMethod() error {
if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
return nil
}
if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
return err
} else if lh != "" {
pt.URI = lh
pt.Pm = PollingLocation
return nil
}
// it's ok if we didn't find a polling header, this will be handled elsewhere
return nil
}
// DELETE
type pollingTrackerDelete struct {
pollingTrackerBase
}
func (pt *pollingTrackerDelete) updatePollingMethod() error {
// for 201 the Location header is required
if pt.resp.StatusCode == http.StatusCreated {
if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
return err
} else if lh == "" {
return autorest.NewError("pollingTrackerDelete", "updateHeaders", "missing Location header in 201 response")
} else {
pt.URI = lh
}
pt.Pm = PollingLocation
pt.FinalGetURI = pt.URI
}
// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
if pt.resp.StatusCode == http.StatusAccepted {
ao, err := getURLFromAsyncOpHeader(pt.resp)
if err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
}
// if the Location header is invalid and we already have a polling URL
// then we don't care if the Location header URL is malformed.
if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
return err
} else if lh != "" {
if ao == "" {
pt.URI = lh
pt.Pm = PollingLocation
}
// when both headers are returned we use the value in the Location header for the final GET
pt.FinalGetURI = lh
}
// make sure a polling URL was found
if pt.URI == "" {
return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
}
}
return nil
}
func (pt pollingTrackerDelete) checkForErrors() error {
return pt.baseCheckForErrors()
}
func (pt pollingTrackerDelete) provisioningStateApplicable() bool {
return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent
}
// PATCH
type pollingTrackerPatch struct {
pollingTrackerBase
}
func (pt *pollingTrackerPatch) updatePollingMethod() error {
// by default we can use the original URL for polling and final GET
if pt.URI == "" {
pt.URI = pt.resp.Request.URL.String()
}
if pt.FinalGetURI == "" {
pt.FinalGetURI = pt.resp.Request.URL.String()
}
if pt.Pm == PollingUnknown {
pt.Pm = PollingRequestURI
}
// for 201 it's permissible for no headers to be returned
if pt.resp.StatusCode == http.StatusCreated {
if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
}
}
// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
// note the absence of the "final GET" mechanism for PATCH
if pt.resp.StatusCode == http.StatusAccepted {
ao, err := getURLFromAsyncOpHeader(pt.resp)
if err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
}
if ao == "" {
if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
return err
} else if lh == "" {
return autorest.NewError("pollingTrackerPatch", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
} else {
pt.URI = lh
pt.Pm = PollingLocation
}
}
}
return nil
}
func (pt pollingTrackerPatch) checkForErrors() error {
return pt.baseCheckForErrors()
}
func (pt pollingTrackerPatch) provisioningStateApplicable() bool {
return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated
}
// POST
type pollingTrackerPost struct {
pollingTrackerBase
}
func (pt *pollingTrackerPost) updatePollingMethod() error {
// 201 requires Location header
if pt.resp.StatusCode == http.StatusCreated {
if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
return err
} else if lh == "" {
return autorest.NewError("pollingTrackerPost", "updateHeaders", "missing Location header in 201 response")
} else {
pt.URI = lh
pt.FinalGetURI = lh
pt.Pm = PollingLocation
}
}
// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
if pt.resp.StatusCode == http.StatusAccepted {
ao, err := getURLFromAsyncOpHeader(pt.resp)
if err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
}
// if the Location header is invalid and we already have a polling URL
// then we don't care if the Location header URL is malformed.
if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
return err
} else if lh != "" {
if ao == "" {
pt.URI = lh
pt.Pm = PollingLocation
}
// when both headers are returned we use the value in the Location header for the final GET
pt.FinalGetURI = lh
}
// make sure a polling URL was found
if pt.URI == "" {
return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
}
}
return nil
}
func (pt pollingTrackerPost) checkForErrors() error {
return pt.baseCheckForErrors()
}
func (pt pollingTrackerPost) provisioningStateApplicable() bool {
return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent
}
// PUT
type pollingTrackerPut struct {
pollingTrackerBase
}
func (pt *pollingTrackerPut) updatePollingMethod() error {
// by default we can use the original URL for polling and final GET
if pt.URI == "" {
pt.URI = pt.resp.Request.URL.String()
}
if pt.FinalGetURI == "" {
pt.FinalGetURI = pt.resp.Request.URL.String()
}
if pt.Pm == PollingUnknown {
pt.Pm = PollingRequestURI
}
// for 201 it's permissible for no headers to be returned
if pt.resp.StatusCode == http.StatusCreated {
if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
}
}
// for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary
if pt.resp.StatusCode == http.StatusAccepted {
ao, err := getURLFromAsyncOpHeader(pt.resp)
if err != nil {
return err
} else if ao != "" {
pt.URI = ao
pt.Pm = PollingAsyncOperation
}
// if the Location header is invalid and we already have a polling URL
// then we don't care if the Location header URL is malformed.
if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
return err
} else if lh != "" {
if ao == "" {
pt.URI = lh
pt.Pm = PollingLocation
}
}
// make sure a polling URL was found
if pt.URI == "" {
return autorest.NewError("pollingTrackerPut", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
}
}
return nil
}
func (pt pollingTrackerPut) checkForErrors() error {
err := pt.baseCheckForErrors()
if err != nil {
return err
}
// if there are no LRO headers then the body cannot be empty
ao, err := getURLFromAsyncOpHeader(pt.resp)
if err != nil {
return err
}
lh, err := getURLFromLocationHeader(pt.resp)
if err != nil {
return err
}
if ao == "" && lh == "" && len(pt.rawBody) == 0 {
return autorest.NewError("pollingTrackerPut", "checkForErrors", "the response did not contain a body")
}
return nil
}
func (pt pollingTrackerPut) provisioningStateApplicable() bool {
return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated
}
// creates a polling tracker based on the verb of the original request
func createPollingTracker(resp *http.Response) (pollingTracker, error) {
var pt pollingTracker
switch strings.ToUpper(resp.Request.Method) {
case http.MethodDelete:
pt = &pollingTrackerDelete{pollingTrackerBase: pollingTrackerBase{resp: resp}}
case http.MethodPatch:
pt = &pollingTrackerPatch{pollingTrackerBase: pollingTrackerBase{resp: resp}}
case http.MethodPost:
pt = &pollingTrackerPost{pollingTrackerBase: pollingTrackerBase{resp: resp}}
case http.MethodPut:
pt = &pollingTrackerPut{pollingTrackerBase: pollingTrackerBase{resp: resp}}
default:
return nil, autorest.NewError("azure", "createPollingTracker", "unsupported HTTP method %s", resp.Request.Method)
}
if err := pt.initializeState(); err != nil {
return pt, err
}
// this initializes the polling header values, we do this during creation in case the
// initial response send us invalid values; this way the API call will return a non-nil
// error (not doing this means the error shows up in Future.Done)
return pt, pt.updatePollingMethod()
}
// gets the polling URL from the Azure-AsyncOperation header.
// ensures the URL is well-formed and absolute.
func getURLFromAsyncOpHeader(resp *http.Response) (string, error) {
s := resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation))
if s == "" {
return "", nil
}
if !isValidURL(s) {
return "", autorest.NewError("azure", "getURLFromAsyncOpHeader", "invalid polling URL '%s'", s)
}
return s, nil
}
// gets the polling URL from the Location header.
// ensures the URL is well-formed and absolute.
func getURLFromLocationHeader(resp *http.Response) (string, error) {
s := resp.Header.Get(http.CanonicalHeaderKey(autorest.HeaderLocation))
if s == "" {
return "", nil
}
if !isValidURL(s) {
return "", autorest.NewError("azure", "getURLFromLocationHeader", "invalid polling URL '%s'", s)
}
return s, nil
}
// verify that the URL is valid and absolute
func isValidURL(s string) bool {
u, err := url.Parse(s)
return err == nil && u.IsAbs()
}
// PollingMethodType defines a type used for enumerating polling mechanisms.
type PollingMethodType string
const (
// PollingAsyncOperation indicates the polling method uses the Azure-AsyncOperation header.
PollingAsyncOperation PollingMethodType = "AsyncOperation"
// PollingLocation indicates the polling method uses the Location header.
PollingLocation PollingMethodType = "Location"
// PollingRequestURI indicates the polling method uses the original request URI.
PollingRequestURI PollingMethodType = "RequestURI"
// PollingUnknown indicates an unknown polling method and is the default value.
PollingUnknown PollingMethodType = ""
)
// AsyncOpIncompleteError is the type that's returned from a future that has not completed.
type AsyncOpIncompleteError struct {
// FutureType is the name of the type composed of a azure.Future.
FutureType string
}
// Error returns an error message including the originating type name of the error.
func (e AsyncOpIncompleteError) Error() string {
return fmt.Sprintf("%s: asynchronous operation has not completed", e.FutureType)
}
// NewAsyncOpIncompleteError creates a new AsyncOpIncompleteError with the specified parameters.
func NewAsyncOpIncompleteError(futureType string) AsyncOpIncompleteError {
return AsyncOpIncompleteError{
FutureType: futureType,
}
}

View File

@@ -1,326 +0,0 @@
// Package azure provides Azure-specific implementations used with AutoRest.
// See the included examples for more detail.
package azure
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/Azure/go-autorest/autorest"
)
const (
// HeaderClientID is the Azure extension header to set a user-specified request ID.
HeaderClientID = "x-ms-client-request-id"
// HeaderReturnClientID is the Azure extension header to set if the user-specified request ID
// should be included in the response.
HeaderReturnClientID = "x-ms-return-client-request-id"
// HeaderRequestID is the Azure extension header of the service generated request ID returned
// in the response.
HeaderRequestID = "x-ms-request-id"
)
// ServiceError encapsulates the error response from an Azure service.
// It adhears to the OData v4 specification for error responses.
type ServiceError struct {
Code string `json:"code"`
Message string `json:"message"`
Target *string `json:"target"`
Details []map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}
func (se ServiceError) Error() string {
result := fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message)
if se.Target != nil {
result += fmt.Sprintf(" Target=%q", *se.Target)
}
if se.Details != nil {
d, err := json.Marshal(se.Details)
if err != nil {
result += fmt.Sprintf(" Details=%v", se.Details)
}
result += fmt.Sprintf(" Details=%v", string(d))
}
if se.InnerError != nil {
d, err := json.Marshal(se.InnerError)
if err != nil {
result += fmt.Sprintf(" InnerError=%v", se.InnerError)
}
result += fmt.Sprintf(" InnerError=%v", string(d))
}
if se.AdditionalInfo != nil {
d, err := json.Marshal(se.AdditionalInfo)
if err != nil {
result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo)
}
result += fmt.Sprintf(" AdditionalInfo=%v", string(d))
}
return result
}
// UnmarshalJSON implements the json.Unmarshaler interface for the ServiceError type.
func (se *ServiceError) UnmarshalJSON(b []byte) error {
// per the OData v4 spec the details field must be an array of JSON objects.
// unfortunately not all services adhear to the spec and just return a single
// object instead of an array with one object. so we have to perform some
// shenanigans to accommodate both cases.
// http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
type serviceError1 struct {
Code string `json:"code"`
Message string `json:"message"`
Target *string `json:"target"`
Details []map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}
type serviceError2 struct {
Code string `json:"code"`
Message string `json:"message"`
Target *string `json:"target"`
Details map[string]interface{} `json:"details"`
InnerError map[string]interface{} `json:"innererror"`
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}
se1 := serviceError1{}
err := json.Unmarshal(b, &se1)
if err == nil {
se.populate(se1.Code, se1.Message, se1.Target, se1.Details, se1.InnerError, se1.AdditionalInfo)
return nil
}
se2 := serviceError2{}
err = json.Unmarshal(b, &se2)
if err == nil {
se.populate(se2.Code, se2.Message, se2.Target, nil, se2.InnerError, se2.AdditionalInfo)
se.Details = append(se.Details, se2.Details)
return nil
}
return err
}
func (se *ServiceError) populate(code, message string, target *string, details []map[string]interface{}, inner map[string]interface{}, additional []map[string]interface{}) {
se.Code = code
se.Message = message
se.Target = target
se.Details = details
se.InnerError = inner
se.AdditionalInfo = additional
}
// RequestError describes an error response returned by Azure service.
type RequestError struct {
autorest.DetailedError
// The error returned by the Azure service.
ServiceError *ServiceError `json:"error"`
// The request id (from the x-ms-request-id-header) of the request.
RequestID string
}
// Error returns a human-friendly error message from service error.
func (e RequestError) Error() string {
return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v",
e.StatusCode, e.ServiceError)
}
// IsAzureError returns true if the passed error is an Azure Service error; false otherwise.
func IsAzureError(e error) bool {
_, ok := e.(*RequestError)
return ok
}
// Resource contains details about an Azure resource.
type Resource struct {
SubscriptionID string
ResourceGroup string
Provider string
ResourceType string
ResourceName string
}
// ParseResourceID parses a resource ID into a ResourceDetails struct.
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#return-value-4.
func ParseResourceID(resourceID string) (Resource, error) {
const resourceIDPatternText = `(?i)subscriptions/(.+)/resourceGroups/(.+)/providers/(.+?)/(.+?)/(.+)`
resourceIDPattern := regexp.MustCompile(resourceIDPatternText)
match := resourceIDPattern.FindStringSubmatch(resourceID)
if len(match) == 0 {
return Resource{}, fmt.Errorf("parsing failed for %s. Invalid resource Id format", resourceID)
}
v := strings.Split(match[5], "/")
resourceName := v[len(v)-1]
result := Resource{
SubscriptionID: match[1],
ResourceGroup: match[2],
Provider: match[3],
ResourceType: match[4],
ResourceName: resourceName,
}
return result, nil
}
// NewErrorWithError creates a new Error conforming object from the
// passed packageType, method, statusCode of the given resp (UndefinedStatusCode
// if resp is nil), message, and original error. message is treated as a format
// string to which the optional args apply.
func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError {
if v, ok := original.(*RequestError); ok {
return *v
}
statusCode := autorest.UndefinedStatusCode
if resp != nil {
statusCode = resp.StatusCode
}
return RequestError{
DetailedError: autorest.DetailedError{
Original: original,
PackageType: packageType,
Method: method,
StatusCode: statusCode,
Message: fmt.Sprintf(message, args...),
},
}
}
// WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of
// x-ms-client-request-id whose value is the passed, undecorated UUID (e.g.,
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id
// header to true such that UUID accompanies the http.Response.
func WithReturningClientID(uuid string) autorest.PrepareDecorator {
preparer := autorest.CreatePreparer(
WithClientID(uuid),
WithReturnClientID(true))
return func(p autorest.Preparer) autorest.Preparer {
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err != nil {
return r, err
}
return preparer.Prepare(r)
})
}
}
// WithClientID returns a PrepareDecorator that adds an HTTP extension header of
// x-ms-client-request-id whose value is passed, undecorated UUID (e.g.,
// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA").
func WithClientID(uuid string) autorest.PrepareDecorator {
return autorest.WithHeader(HeaderClientID, uuid)
}
// WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of
// x-ms-return-client-request-id whose boolean value indicates if the value of the
// x-ms-client-request-id header should be included in the http.Response.
func WithReturnClientID(b bool) autorest.PrepareDecorator {
return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b))
}
// ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the
// http.Request sent to the service (and returned in the http.Response)
func ExtractClientID(resp *http.Response) string {
return autorest.ExtractHeaderValue(HeaderClientID, resp)
}
// ExtractRequestID extracts the Azure server generated request identifier from the
// x-ms-request-id header.
func ExtractRequestID(resp *http.Response) string {
return autorest.ExtractHeaderValue(HeaderRequestID, resp)
}
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an
// azure.RequestError by reading the response body unless the response HTTP status code
// is among the set passed.
//
// If there is a chance service may return responses other than the Azure error
// format and the response cannot be parsed into an error, a decoding error will
// be returned containing the response body. In any case, the Responder will
// return an error if the status code is not satisfied.
//
// If this Responder returns an error, the response body will be replaced with
// an in-memory reader, which needs no further closing.
func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
return func(r autorest.Responder) autorest.Responder {
return autorest.ResponderFunc(func(resp *http.Response) error {
err := r.Respond(resp)
if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
var e RequestError
defer resp.Body.Close()
// Copy and replace the Body in case it does not contain an error object.
// This will leave the Body available to the caller.
b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e)
resp.Body = ioutil.NopCloser(&b)
if decodeErr != nil {
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
}
if e.ServiceError == nil {
// Check if error is unwrapped ServiceError
if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil {
return err
}
}
if e.ServiceError.Message == "" {
// if we're here it means the returned error wasn't OData v4 compliant.
// try to unmarshal the body as raw JSON in hopes of getting something.
rawBody := map[string]interface{}{}
if err := json.Unmarshal(b.Bytes(), &rawBody); err != nil {
return err
}
e.ServiceError = &ServiceError{
Code: "Unknown",
Message: "Unknown service error",
}
if len(rawBody) > 0 {
e.ServiceError.Details = []map[string]interface{}{rawBody}
}
}
e.Response = resp
e.RequestID = ExtractRequestID(resp)
if e.StatusCode == nil {
e.StatusCode = resp.StatusCode
}
err = &e
}
return err
})
}
}

View File

@@ -1,244 +0,0 @@
package azure
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
)
const (
// EnvironmentFilepathName captures the name of the environment variable containing the path to the file
// to be used while populating the Azure Environment.
EnvironmentFilepathName = "AZURE_ENVIRONMENT_FILEPATH"
// NotAvailable is used for endpoints and resource IDs that are not available for a given cloud.
NotAvailable = "N/A"
)
var environments = map[string]Environment{
"AZURECHINACLOUD": ChinaCloud,
"AZUREGERMANCLOUD": GermanCloud,
"AZUREPUBLICCLOUD": PublicCloud,
"AZUREUSGOVERNMENTCLOUD": USGovernmentCloud,
}
// ResourceIdentifier contains a set of Azure resource IDs.
type ResourceIdentifier struct {
Graph string `json:"graph"`
KeyVault string `json:"keyVault"`
Datalake string `json:"datalake"`
Batch string `json:"batch"`
OperationalInsights string `json:"operationalInsights"`
Storage string `json:"storage"`
}
// Environment represents a set of endpoints for each of Azure's Clouds.
type Environment struct {
Name string `json:"name"`
ManagementPortalURL string `json:"managementPortalURL"`
PublishSettingsURL string `json:"publishSettingsURL"`
ServiceManagementEndpoint string `json:"serviceManagementEndpoint"`
ResourceManagerEndpoint string `json:"resourceManagerEndpoint"`
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
GalleryEndpoint string `json:"galleryEndpoint"`
KeyVaultEndpoint string `json:"keyVaultEndpoint"`
GraphEndpoint string `json:"graphEndpoint"`
ServiceBusEndpoint string `json:"serviceBusEndpoint"`
BatchManagementEndpoint string `json:"batchManagementEndpoint"`
StorageEndpointSuffix string `json:"storageEndpointSuffix"`
SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"`
TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"`
KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"`
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
TokenAudience string `json:"tokenAudience"`
ResourceIdentifiers ResourceIdentifier `json:"resourceIdentifiers"`
}
var (
// PublicCloud is the default public Azure cloud environment
PublicCloud = Environment{
Name: "AzurePublicCloud",
ManagementPortalURL: "https://manage.windowsazure.com/",
PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.windows.net/",
ResourceManagerEndpoint: "https://management.azure.com/",
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
GalleryEndpoint: "https://gallery.azure.com/",
KeyVaultEndpoint: "https://vault.azure.net/",
GraphEndpoint: "https://graph.windows.net/",
ServiceBusEndpoint: "https://servicebus.windows.net/",
BatchManagementEndpoint: "https://batch.core.windows.net/",
StorageEndpointSuffix: "core.windows.net",
SQLDatabaseDNSSuffix: "database.windows.net",
TrafficManagerDNSSuffix: "trafficmanager.net",
KeyVaultDNSSuffix: "vault.azure.net",
ServiceBusEndpointSuffix: "servicebus.windows.net",
ServiceManagementVMDNSSuffix: "cloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.azure.com",
ContainerRegistryDNSSuffix: "azurecr.io",
CosmosDBDNSSuffix: "documents.azure.com",
TokenAudience: "https://management.azure.com/",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.windows.net/",
KeyVault: "https://vault.azure.net",
Datalake: "https://datalake.azure.net/",
Batch: "https://batch.core.windows.net/",
OperationalInsights: "https://api.loganalytics.io",
Storage: "https://storage.azure.com/",
},
}
// USGovernmentCloud is the cloud environment for the US Government
USGovernmentCloud = Environment{
Name: "AzureUSGovernmentCloud",
ManagementPortalURL: "https://manage.windowsazure.us/",
PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/",
ResourceManagerEndpoint: "https://management.usgovcloudapi.net/",
ActiveDirectoryEndpoint: "https://login.microsoftonline.us/",
GalleryEndpoint: "https://gallery.usgovcloudapi.net/",
KeyVaultEndpoint: "https://vault.usgovcloudapi.net/",
GraphEndpoint: "https://graph.windows.net/",
ServiceBusEndpoint: "https://servicebus.usgovcloudapi.net/",
BatchManagementEndpoint: "https://batch.core.usgovcloudapi.net/",
StorageEndpointSuffix: "core.usgovcloudapi.net",
SQLDatabaseDNSSuffix: "database.usgovcloudapi.net",
TrafficManagerDNSSuffix: "usgovtrafficmanager.net",
KeyVaultDNSSuffix: "vault.usgovcloudapi.net",
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us",
ContainerRegistryDNSSuffix: "azurecr.us",
CosmosDBDNSSuffix: "documents.azure.us",
TokenAudience: "https://management.usgovcloudapi.net/",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.windows.net/",
KeyVault: "https://vault.usgovcloudapi.net",
Datalake: NotAvailable,
Batch: "https://batch.core.usgovcloudapi.net/",
OperationalInsights: "https://api.loganalytics.us",
Storage: "https://storage.azure.com/",
},
}
// ChinaCloud is the cloud environment operated in China
ChinaCloud = Environment{
Name: "AzureChinaCloud",
ManagementPortalURL: "https://manage.chinacloudapi.com/",
PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/",
ResourceManagerEndpoint: "https://management.chinacloudapi.cn/",
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/",
GalleryEndpoint: "https://gallery.chinacloudapi.cn/",
KeyVaultEndpoint: "https://vault.azure.cn/",
GraphEndpoint: "https://graph.chinacloudapi.cn/",
ServiceBusEndpoint: "https://servicebus.chinacloudapi.cn/",
BatchManagementEndpoint: "https://batch.chinacloudapi.cn/",
StorageEndpointSuffix: "core.chinacloudapi.cn",
SQLDatabaseDNSSuffix: "database.chinacloudapi.cn",
TrafficManagerDNSSuffix: "trafficmanager.cn",
KeyVaultDNSSuffix: "vault.azure.cn",
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn",
ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
ResourceManagerVMDNSSuffix: "cloudapp.azure.cn",
ContainerRegistryDNSSuffix: "azurecr.cn",
CosmosDBDNSSuffix: "documents.azure.cn",
TokenAudience: "https://management.chinacloudapi.cn/",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.chinacloudapi.cn/",
KeyVault: "https://vault.azure.cn",
Datalake: NotAvailable,
Batch: "https://batch.chinacloudapi.cn/",
OperationalInsights: NotAvailable,
Storage: "https://storage.azure.com/",
},
}
// GermanCloud is the cloud environment operated in Germany
GermanCloud = Environment{
Name: "AzureGermanCloud",
ManagementPortalURL: "http://portal.microsoftazure.de/",
PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index",
ServiceManagementEndpoint: "https://management.core.cloudapi.de/",
ResourceManagerEndpoint: "https://management.microsoftazure.de/",
ActiveDirectoryEndpoint: "https://login.microsoftonline.de/",
GalleryEndpoint: "https://gallery.cloudapi.de/",
KeyVaultEndpoint: "https://vault.microsoftazure.de/",
GraphEndpoint: "https://graph.cloudapi.de/",
ServiceBusEndpoint: "https://servicebus.cloudapi.de/",
BatchManagementEndpoint: "https://batch.cloudapi.de/",
StorageEndpointSuffix: "core.cloudapi.de",
SQLDatabaseDNSSuffix: "database.cloudapi.de",
TrafficManagerDNSSuffix: "azuretrafficmanager.de",
KeyVaultDNSSuffix: "vault.microsoftazure.de",
ServiceBusEndpointSuffix: "servicebus.cloudapi.de",
ServiceManagementVMDNSSuffix: "azurecloudapp.de",
ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de",
ContainerRegistryDNSSuffix: NotAvailable,
CosmosDBDNSSuffix: "documents.microsoftazure.de",
TokenAudience: "https://management.microsoftazure.de/",
ResourceIdentifiers: ResourceIdentifier{
Graph: "https://graph.cloudapi.de/",
KeyVault: "https://vault.microsoftazure.de",
Datalake: NotAvailable,
Batch: "https://batch.cloudapi.de/",
OperationalInsights: NotAvailable,
Storage: "https://storage.azure.com/",
},
}
)
// EnvironmentFromName returns an Environment based on the common name specified.
func EnvironmentFromName(name string) (Environment, error) {
// IMPORTANT
// As per @radhikagupta5:
// This is technical debt, fundamentally here because Kubernetes is not currently accepting
// contributions to the providers. Once that is an option, the provider should be updated to
// directly call `EnvironmentFromFile`. Until then, we rely on dispatching Azure Stack environment creation
// from this method based on the name that is provided to us.
if strings.EqualFold(name, "AZURESTACKCLOUD") {
return EnvironmentFromFile(os.Getenv(EnvironmentFilepathName))
}
name = strings.ToUpper(name)
env, ok := environments[name]
if !ok {
return env, fmt.Errorf("autorest/azure: There is no cloud environment matching the name %q", name)
}
return env, nil
}
// EnvironmentFromFile loads an Environment from a configuration file available on disk.
// This function is particularly useful in the Hybrid Cloud model, where one must define their own
// endpoints.
func EnvironmentFromFile(location string) (unmarshaled Environment, err error) {
fileContents, err := ioutil.ReadFile(location)
if err != nil {
return
}
err = json.Unmarshal(fileContents, &unmarshaled)
return
}

View File

@@ -1,245 +0,0 @@
package azure
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/Azure/go-autorest/autorest"
)
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
type audience []string
type authentication struct {
LoginEndpoint string `json:"loginEndpoint"`
Audiences audience `json:"audiences"`
}
type environmentMetadataInfo struct {
GalleryEndpoint string `json:"galleryEndpoint"`
GraphEndpoint string `json:"graphEndpoint"`
PortalEndpoint string `json:"portalEndpoint"`
Authentication authentication `json:"authentication"`
}
// EnvironmentProperty represent property names that clients can override
type EnvironmentProperty string
const (
// EnvironmentName ...
EnvironmentName EnvironmentProperty = "name"
// EnvironmentManagementPortalURL ..
EnvironmentManagementPortalURL EnvironmentProperty = "managementPortalURL"
// EnvironmentPublishSettingsURL ...
EnvironmentPublishSettingsURL EnvironmentProperty = "publishSettingsURL"
// EnvironmentServiceManagementEndpoint ...
EnvironmentServiceManagementEndpoint EnvironmentProperty = "serviceManagementEndpoint"
// EnvironmentResourceManagerEndpoint ...
EnvironmentResourceManagerEndpoint EnvironmentProperty = "resourceManagerEndpoint"
// EnvironmentActiveDirectoryEndpoint ...
EnvironmentActiveDirectoryEndpoint EnvironmentProperty = "activeDirectoryEndpoint"
// EnvironmentGalleryEndpoint ...
EnvironmentGalleryEndpoint EnvironmentProperty = "galleryEndpoint"
// EnvironmentKeyVaultEndpoint ...
EnvironmentKeyVaultEndpoint EnvironmentProperty = "keyVaultEndpoint"
// EnvironmentGraphEndpoint ...
EnvironmentGraphEndpoint EnvironmentProperty = "graphEndpoint"
// EnvironmentServiceBusEndpoint ...
EnvironmentServiceBusEndpoint EnvironmentProperty = "serviceBusEndpoint"
// EnvironmentBatchManagementEndpoint ...
EnvironmentBatchManagementEndpoint EnvironmentProperty = "batchManagementEndpoint"
// EnvironmentStorageEndpointSuffix ...
EnvironmentStorageEndpointSuffix EnvironmentProperty = "storageEndpointSuffix"
// EnvironmentSQLDatabaseDNSSuffix ...
EnvironmentSQLDatabaseDNSSuffix EnvironmentProperty = "sqlDatabaseDNSSuffix"
// EnvironmentTrafficManagerDNSSuffix ...
EnvironmentTrafficManagerDNSSuffix EnvironmentProperty = "trafficManagerDNSSuffix"
// EnvironmentKeyVaultDNSSuffix ...
EnvironmentKeyVaultDNSSuffix EnvironmentProperty = "keyVaultDNSSuffix"
// EnvironmentServiceBusEndpointSuffix ...
EnvironmentServiceBusEndpointSuffix EnvironmentProperty = "serviceBusEndpointSuffix"
// EnvironmentServiceManagementVMDNSSuffix ...
EnvironmentServiceManagementVMDNSSuffix EnvironmentProperty = "serviceManagementVMDNSSuffix"
// EnvironmentResourceManagerVMDNSSuffix ...
EnvironmentResourceManagerVMDNSSuffix EnvironmentProperty = "resourceManagerVMDNSSuffix"
// EnvironmentContainerRegistryDNSSuffix ...
EnvironmentContainerRegistryDNSSuffix EnvironmentProperty = "containerRegistryDNSSuffix"
// EnvironmentTokenAudience ...
EnvironmentTokenAudience EnvironmentProperty = "tokenAudience"
)
// OverrideProperty represents property name and value that clients can override
type OverrideProperty struct {
Key EnvironmentProperty
Value string
}
// EnvironmentFromURL loads an Environment from a URL
// This function is particularly useful in the Hybrid Cloud model, where one may define their own
// endpoints.
func EnvironmentFromURL(resourceManagerEndpoint string, properties ...OverrideProperty) (environment Environment, err error) {
var metadataEnvProperties environmentMetadataInfo
if resourceManagerEndpoint == "" {
return environment, fmt.Errorf("Metadata resource manager endpoint is empty")
}
if metadataEnvProperties, err = retrieveMetadataEnvironment(resourceManagerEndpoint); err != nil {
return environment, err
}
// Give priority to user's override values
overrideProperties(&environment, properties)
if environment.Name == "" {
environment.Name = "HybridEnvironment"
}
stampDNSSuffix := environment.StorageEndpointSuffix
if stampDNSSuffix == "" {
stampDNSSuffix = strings.TrimSuffix(strings.TrimPrefix(strings.Replace(resourceManagerEndpoint, strings.Split(resourceManagerEndpoint, ".")[0], "", 1), "."), "/")
environment.StorageEndpointSuffix = stampDNSSuffix
}
if environment.KeyVaultDNSSuffix == "" {
environment.KeyVaultDNSSuffix = fmt.Sprintf("%s.%s", "vault", stampDNSSuffix)
}
if environment.KeyVaultEndpoint == "" {
environment.KeyVaultEndpoint = fmt.Sprintf("%s%s", "https://", environment.KeyVaultDNSSuffix)
}
if environment.TokenAudience == "" {
environment.TokenAudience = metadataEnvProperties.Authentication.Audiences[0]
}
if environment.ActiveDirectoryEndpoint == "" {
environment.ActiveDirectoryEndpoint = metadataEnvProperties.Authentication.LoginEndpoint
}
if environment.ResourceManagerEndpoint == "" {
environment.ResourceManagerEndpoint = resourceManagerEndpoint
}
if environment.GalleryEndpoint == "" {
environment.GalleryEndpoint = metadataEnvProperties.GalleryEndpoint
}
if environment.GraphEndpoint == "" {
environment.GraphEndpoint = metadataEnvProperties.GraphEndpoint
}
return environment, nil
}
func overrideProperties(environment *Environment, properties []OverrideProperty) {
for _, property := range properties {
switch property.Key {
case EnvironmentName:
{
environment.Name = property.Value
}
case EnvironmentManagementPortalURL:
{
environment.ManagementPortalURL = property.Value
}
case EnvironmentPublishSettingsURL:
{
environment.PublishSettingsURL = property.Value
}
case EnvironmentServiceManagementEndpoint:
{
environment.ServiceManagementEndpoint = property.Value
}
case EnvironmentResourceManagerEndpoint:
{
environment.ResourceManagerEndpoint = property.Value
}
case EnvironmentActiveDirectoryEndpoint:
{
environment.ActiveDirectoryEndpoint = property.Value
}
case EnvironmentGalleryEndpoint:
{
environment.GalleryEndpoint = property.Value
}
case EnvironmentKeyVaultEndpoint:
{
environment.KeyVaultEndpoint = property.Value
}
case EnvironmentGraphEndpoint:
{
environment.GraphEndpoint = property.Value
}
case EnvironmentServiceBusEndpoint:
{
environment.ServiceBusEndpoint = property.Value
}
case EnvironmentBatchManagementEndpoint:
{
environment.BatchManagementEndpoint = property.Value
}
case EnvironmentStorageEndpointSuffix:
{
environment.StorageEndpointSuffix = property.Value
}
case EnvironmentSQLDatabaseDNSSuffix:
{
environment.SQLDatabaseDNSSuffix = property.Value
}
case EnvironmentTrafficManagerDNSSuffix:
{
environment.TrafficManagerDNSSuffix = property.Value
}
case EnvironmentKeyVaultDNSSuffix:
{
environment.KeyVaultDNSSuffix = property.Value
}
case EnvironmentServiceBusEndpointSuffix:
{
environment.ServiceBusEndpointSuffix = property.Value
}
case EnvironmentServiceManagementVMDNSSuffix:
{
environment.ServiceManagementVMDNSSuffix = property.Value
}
case EnvironmentResourceManagerVMDNSSuffix:
{
environment.ResourceManagerVMDNSSuffix = property.Value
}
case EnvironmentContainerRegistryDNSSuffix:
{
environment.ContainerRegistryDNSSuffix = property.Value
}
case EnvironmentTokenAudience:
{
environment.TokenAudience = property.Value
}
}
}
}
func retrieveMetadataEnvironment(endpoint string) (environment environmentMetadataInfo, err error) {
client := autorest.NewClientWithUserAgent("")
managementEndpoint := fmt.Sprintf("%s%s", strings.TrimSuffix(endpoint, "/"), "/metadata/endpoints?api-version=1.0")
req, _ := http.NewRequest("GET", managementEndpoint, nil)
response, err := client.Do(req)
if err != nil {
return environment, err
}
defer response.Body.Close()
jsonResponse, err := ioutil.ReadAll(response.Body)
if err != nil {
return environment, err
}
err = json.Unmarshal(jsonResponse, &environment)
return environment, err
}

View File

@@ -1,200 +0,0 @@
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package azure
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/Azure/go-autorest/autorest"
)
// DoRetryWithRegistration tries to register the resource provider in case it is unregistered.
// It also handles request retries
func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator {
return func(s autorest.Sender) autorest.Sender {
return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
rr := autorest.NewRetriableRequest(r)
for currentAttempt := 0; currentAttempt < client.RetryAttempts; currentAttempt++ {
err = rr.Prepare()
if err != nil {
return resp, err
}
resp, err = autorest.SendWithSender(s, rr.Request(),
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
)
if err != nil {
return resp, err
}
if resp.StatusCode != http.StatusConflict || client.SkipResourceProviderRegistration {
return resp, err
}
var re RequestError
err = autorest.Respond(
resp,
autorest.ByUnmarshallingJSON(&re),
)
if err != nil {
return resp, err
}
err = re
if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" {
regErr := register(client, r, re)
if regErr != nil {
return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %s", regErr, err)
}
}
}
return resp, err
})
}
}
func getProvider(re RequestError) (string, error) {
if re.ServiceError != nil && len(re.ServiceError.Details) > 0 {
return re.ServiceError.Details[0]["target"].(string), nil
}
return "", errors.New("provider was not found in the response")
}
func register(client autorest.Client, originalReq *http.Request, re RequestError) error {
subID := getSubscription(originalReq.URL.Path)
if subID == "" {
return errors.New("missing parameter subscriptionID to register resource provider")
}
providerName, err := getProvider(re)
if err != nil {
return fmt.Errorf("missing parameter provider to register resource provider: %s", err)
}
newURL := url.URL{
Scheme: originalReq.URL.Scheme,
Host: originalReq.URL.Host,
}
// taken from the resources SDK
// with almost identical code, this sections are easier to mantain
// It is also not a good idea to import the SDK here
// https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L252
pathParameters := map[string]interface{}{
"resourceProviderNamespace": autorest.Encode("path", providerName),
"subscriptionId": autorest.Encode("path", subID),
}
const APIVersion = "2016-09-01"
queryParameters := map[string]interface{}{
"api-version": APIVersion,
}
preparer := autorest.CreatePreparer(
autorest.AsPost(),
autorest.WithBaseURL(newURL.String()),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters),
autorest.WithQueryParameters(queryParameters),
)
req, err := preparer.Prepare(&http.Request{})
if err != nil {
return err
}
req = req.WithContext(originalReq.Context())
resp, err := autorest.SendWithSender(client, req,
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
)
if err != nil {
return err
}
type Provider struct {
RegistrationState *string `json:"registrationState,omitempty"`
}
var provider Provider
err = autorest.Respond(
resp,
WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&provider),
autorest.ByClosing(),
)
if err != nil {
return err
}
// poll for registered provisioning state
registrationStartTime := time.Now()
for err == nil && (client.PollingDuration == 0 || (client.PollingDuration != 0 && time.Since(registrationStartTime) < client.PollingDuration)) {
// taken from the resources SDK
// https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L45
preparer := autorest.CreatePreparer(
autorest.AsGet(),
autorest.WithBaseURL(newURL.String()),
autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters),
autorest.WithQueryParameters(queryParameters),
)
req, err = preparer.Prepare(&http.Request{})
if err != nil {
return err
}
req = req.WithContext(originalReq.Context())
resp, err := autorest.SendWithSender(client, req,
autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...),
)
if err != nil {
return err
}
err = autorest.Respond(
resp,
WithErrorUnlessStatusCode(http.StatusOK),
autorest.ByUnmarshallingJSON(&provider),
autorest.ByClosing(),
)
if err != nil {
return err
}
if provider.RegistrationState != nil &&
*provider.RegistrationState == "Registered" {
break
}
delayed := autorest.DelayWithRetryAfter(resp, originalReq.Context().Done())
if !delayed && !autorest.DelayForBackoff(client.PollingDelay, 0, originalReq.Context().Done()) {
return originalReq.Context().Err()
}
}
if client.PollingDuration != 0 && !(time.Since(registrationStartTime) < client.PollingDuration) {
return errors.New("polling for resource provider registration has exceeded the polling duration")
}
return err
}
func getSubscription(path string) string {
parts := strings.Split(path, "/")
for i, v := range parts {
if v == "subscriptions" && (i+1) < len(parts) {
return parts[i+1]
}
}
return ""
}

View File

@@ -1,300 +0,0 @@
package autorest
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
"github.com/Azure/go-autorest/logger"
)
const (
// DefaultPollingDelay is a reasonable delay between polling requests.
DefaultPollingDelay = 60 * time.Second
// DefaultPollingDuration is a reasonable total polling duration.
DefaultPollingDuration = 15 * time.Minute
// DefaultRetryAttempts is number of attempts for retry status codes (5xx).
DefaultRetryAttempts = 3
// DefaultRetryDuration is the duration to wait between retries.
DefaultRetryDuration = 30 * time.Second
)
var (
// StatusCodesForRetry are a defined group of status code for which the client will retry
StatusCodesForRetry = []int{
http.StatusRequestTimeout, // 408
http.StatusTooManyRequests, // 429
http.StatusInternalServerError, // 500
http.StatusBadGateway, // 502
http.StatusServiceUnavailable, // 503
http.StatusGatewayTimeout, // 504
}
)
const (
requestFormat = `HTTP Request Begin ===================================================
%s
===================================================== HTTP Request End
`
responseFormat = `HTTP Response Begin ===================================================
%s
===================================================== HTTP Response End
`
)
// Response serves as the base for all responses from generated clients. It provides access to the
// last http.Response.
type Response struct {
*http.Response `json:"-"`
}
// IsHTTPStatus returns true if the returned HTTP status code matches the provided status code.
// If there was no response (i.e. the underlying http.Response is nil) the return value is false.
func (r Response) IsHTTPStatus(statusCode int) bool {
if r.Response == nil {
return false
}
return r.Response.StatusCode == statusCode
}
// HasHTTPStatus returns true if the returned HTTP status code matches one of the provided status codes.
// If there was no response (i.e. the underlying http.Response is nil) or not status codes are provided
// the return value is false.
func (r Response) HasHTTPStatus(statusCodes ...int) bool {
return ResponseHasStatusCode(r.Response, statusCodes...)
}
// LoggingInspector implements request and response inspectors that log the full request and
// response to a supplied log.
type LoggingInspector struct {
Logger *log.Logger
}
// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The
// body is restored after being emitted.
//
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
// important. It is best used to trace JSON or similar body values.
func (li LoggingInspector) WithInspection() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
var body, b bytes.Buffer
defer r.Body.Close()
r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body))
if err := r.Write(&b); err != nil {
return nil, fmt.Errorf("Failed to write response: %v", err)
}
li.Logger.Printf(requestFormat, b.String())
r.Body = ioutil.NopCloser(&body)
return p.Prepare(r)
})
}
}
// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The
// body is restored after being emitted.
//
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
// important. It is best used to trace JSON or similar body values.
func (li LoggingInspector) ByInspecting() RespondDecorator {
return func(r Responder) Responder {
return ResponderFunc(func(resp *http.Response) error {
var body, b bytes.Buffer
defer resp.Body.Close()
resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body))
if err := resp.Write(&b); err != nil {
return fmt.Errorf("Failed to write response: %v", err)
}
li.Logger.Printf(responseFormat, b.String())
resp.Body = ioutil.NopCloser(&body)
return r.Respond(resp)
})
}
}
// Client is the base for autorest generated clients. It provides default, "do nothing"
// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the
// standard, undecorated http.Client as a default Sender.
//
// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and
// return responses that compose with Response.
//
// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom
// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit
// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence
// sending the request by providing a decorated Sender.
type Client struct {
Authorizer Authorizer
Sender Sender
RequestInspector PrepareDecorator
ResponseInspector RespondDecorator
// PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header
PollingDelay time.Duration
// PollingDuration sets the maximum polling time after which an error is returned.
// Setting this to zero will use the provided context to control the duration.
PollingDuration time.Duration
// RetryAttempts sets the default number of retry attempts for client.
RetryAttempts int
// RetryDuration sets the delay duration for retries.
RetryDuration time.Duration
// UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent
// through the Do method.
UserAgent string
Jar http.CookieJar
// Set to true to skip attempted registration of resource providers (false by default).
SkipResourceProviderRegistration bool
}
// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
// string.
func NewClientWithUserAgent(ua string) Client {
return newClient(ua, tls.RenegotiateNever)
}
// ClientOptions contains various Client configuration options.
type ClientOptions struct {
// UserAgent is an optional user-agent string to append to the default user agent.
UserAgent string
// Renegotiation is an optional setting to control client-side TLS renegotiation.
Renegotiation tls.RenegotiationSupport
}
// NewClientWithOptions returns an instance of a Client with the specified values.
func NewClientWithOptions(options ClientOptions) Client {
return newClient(options.UserAgent, options.Renegotiation)
}
func newClient(ua string, renegotiation tls.RenegotiationSupport) Client {
c := Client{
PollingDelay: DefaultPollingDelay,
PollingDuration: DefaultPollingDuration,
RetryAttempts: DefaultRetryAttempts,
RetryDuration: DefaultRetryDuration,
UserAgent: UserAgent(),
}
c.Sender = c.sender(renegotiation)
c.AddToUserAgent(ua)
return c
}
// AddToUserAgent adds an extension to the current user agent
func (c *Client) AddToUserAgent(extension string) error {
if extension != "" {
c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
return nil
}
return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
}
// Do implements the Sender interface by invoking the active Sender after applying authorization.
// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent
// is set, apply set the User-Agent header.
func (c Client) Do(r *http.Request) (*http.Response, error) {
if r.UserAgent() == "" {
r, _ = Prepare(r,
WithUserAgent(c.UserAgent))
}
// NOTE: c.WithInspection() must be last in the list so that it can inspect all preceding operations
r, err := Prepare(r,
c.WithAuthorization(),
c.WithInspection())
if err != nil {
var resp *http.Response
if detErr, ok := err.(DetailedError); ok {
// if the authorization failed (e.g. invalid credentials) there will
// be a response associated with the error, be sure to return it.
resp = detErr.Response
}
return resp, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed")
}
logger.Instance.WriteRequest(r, logger.Filter{
Header: func(k string, v []string) (bool, []string) {
// remove the auth token from the log
if strings.EqualFold(k, "Authorization") || strings.EqualFold(k, "Ocp-Apim-Subscription-Key") {
v = []string{"**REDACTED**"}
}
return true, v
},
})
resp, err := SendWithSender(c.sender(tls.RenegotiateNever), r)
logger.Instance.WriteResponse(resp, logger.Filter{})
Respond(resp, c.ByInspecting())
return resp, err
}
// sender returns the Sender to which to send requests.
func (c Client) sender(renengotiation tls.RenegotiationSupport) Sender {
if c.Sender == nil {
return sender(renengotiation)
}
return c.Sender
}
// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator
// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer.
func (c Client) WithAuthorization() PrepareDecorator {
return c.authorizer().WithAuthorization()
}
// authorizer returns the Authorizer to use.
func (c Client) authorizer() Authorizer {
if c.Authorizer == nil {
return NullAuthorizer{}
}
return c.Authorizer
}
// WithInspection is a convenience method that passes the request to the supplied RequestInspector,
// if present, or returns the WithNothing PrepareDecorator otherwise.
func (c Client) WithInspection() PrepareDecorator {
if c.RequestInspector == nil {
return WithNothing()
}
return c.RequestInspector
}
// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector,
// if present, or returns the ByIgnoring RespondDecorator otherwise.
func (c Client) ByInspecting() RespondDecorator {
if c.ResponseInspector == nil {
return ByIgnoring()
}
return c.ResponseInspector
}

View File

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

View File

@@ -1,96 +0,0 @@
/*
Package date provides time.Time derivatives that conform to the Swagger.io (https://swagger.io/)
defined date formats: Date and DateTime. Both types may, in most cases, be used in lieu of
time.Time types. And both convert to time.Time through a ToTime method.
*/
package date
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"fmt"
"time"
)
const (
fullDate = "2006-01-02"
fullDateJSON = `"2006-01-02"`
dateFormat = "%04d-%02d-%02d"
jsonFormat = `"%04d-%02d-%02d"`
)
// Date defines a type similar to time.Time but assumes a layout of RFC3339 full-date (i.e.,
// 2006-01-02).
type Date struct {
time.Time
}
// ParseDate create a new Date from the passed string.
func ParseDate(date string) (d Date, err error) {
return parseDate(date, fullDate)
}
func parseDate(date string, format string) (Date, error) {
d, err := time.Parse(format, date)
return Date{Time: d}, err
}
// MarshalBinary preserves the Date as a byte array conforming to RFC3339 full-date (i.e.,
// 2006-01-02).
func (d Date) MarshalBinary() ([]byte, error) {
return d.MarshalText()
}
// UnmarshalBinary reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e.,
// 2006-01-02).
func (d *Date) UnmarshalBinary(data []byte) error {
return d.UnmarshalText(data)
}
// MarshalJSON preserves the Date as a JSON string conforming to RFC3339 full-date (i.e.,
// 2006-01-02).
func (d Date) MarshalJSON() (json []byte, err error) {
return []byte(fmt.Sprintf(jsonFormat, d.Year(), d.Month(), d.Day())), nil
}
// UnmarshalJSON reconstitutes the Date from a JSON string conforming to RFC3339 full-date (i.e.,
// 2006-01-02).
func (d *Date) UnmarshalJSON(data []byte) (err error) {
d.Time, err = time.Parse(fullDateJSON, string(data))
return err
}
// MarshalText preserves the Date as a byte array conforming to RFC3339 full-date (i.e.,
// 2006-01-02).
func (d Date) MarshalText() (text []byte, err error) {
return []byte(fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())), nil
}
// UnmarshalText reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e.,
// 2006-01-02).
func (d *Date) UnmarshalText(data []byte) (err error) {
d.Time, err = time.Parse(fullDate, string(data))
return err
}
// String returns the Date formatted as an RFC3339 full-date string (i.e., 2006-01-02).
func (d Date) String() string {
return fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())
}
// ToTime returns a Date as a time.Time
func (d Date) ToTime() time.Time {
return d.Time
}

View File

@@ -1,3 +0,0 @@
module github.com/Azure/go-autorest/autorest/date
go 1.12

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