未验证 提交 5b2d1b93 编写于 作者: R runzexia

Merge branch 'master' into bitbucket-support

pkg/cmd/api/spec/api.swagger.json linguist-generated=true
pkg/cmd/api/spec/static.go linguist-generated=true
pkg/client/* linguist-generated=true
config/crds/* linguist-generated=true
config/rbac/* linguist-generated=true
zz_generated.deepcopy.go linguist-generated=true
\ No newline at end of file
......@@ -28,3 +28,5 @@ apiserver.local.config
.DS_Store
api.json
*.coverprofile
kustomize/network/etcd
\ No newline at end of file
......@@ -94,7 +94,7 @@ clean:
# find or download controller-gen
# download controller-gen if necessary
clientset: generate
clientset:
./hack/generate_client.sh
......@@ -106,7 +106,7 @@ internal-crds:
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/apis/network/..." output:crd:artifacts:config=config/crd/bases
internal-generate-apis: internal-controller-gen
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/network/...
internal-controller-gen:
ifeq (, $(shell which controller-gen))
......
API rule violation: names_match,./devops/v1alpha1,S2iBinarySpec,MD5
API rule violation: names_match,k8s.io/api/core/v1,AzureDiskVolumeSource,DataDiskURI
API rule violation: names_match,k8s.io/api/core/v1,ContainerStatus,LastTerminationState
API rule violation: names_match,k8s.io/api/core/v1,DaemonEndpoint,Port
......@@ -44,4 +45,3 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,Raw
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,IntVal
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,StrVal
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type
API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1,S2iBinarySpec,MD5
FROM gcr.io/distroless/static:latest
WORKDIR /
COPY ks-network .
ENTRYPOINT ["/ks-network"]
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")
//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())
}
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: namespacenetworkpolicy-sample
spec:
# Add fields here
foo: bar
......@@ -5,6 +5,7 @@ go 1.12
require (
bitbucket.org/ww/goautoneg v0.0.0-20120707110453-75cd24fc2f2c // indirect
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Azure/go-autorest/autorest v0.5.0 // indirect
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/PuerkitoBio/goquery v1.5.0
......@@ -36,9 +37,14 @@ require (
github.com/evanphx/json-patch v4.2.0+incompatible // 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 // indirect
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1 // indirect
github.com/go-openapi/spec v0.19.2
github.com/go-openapi/jsonpointer v0.19.0 // indirect
github.com/go-openapi/jsonreference v0.19.0 // indirect
github.com/go-openapi/spec v0.19.0
github.com/go-openapi/swag v0.19.0 // indirect
github.com/go-playground/locales v0.12.1 // indirect
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/gobuffalo/flect v0.1.5 // indirect
......@@ -46,12 +52,12 @@ require (
github.com/golang/example v0.0.0-20170904185048-46695d81d1fa
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/gofuzz v1.0.0 // indirect
github.com/google/uuid v1.1.1
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gophercloud/gophercloud v0.3.0 // indirect
github.com/gorilla/mux v1.7.1 // indirect
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
......@@ -64,6 +70,7 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/json-iterator/go v1.1.6
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kiali/kiali v1.1.0
github.com/klauspost/cpuid v1.2.1 // indirect
github.com/knative/pkg v0.0.0-20190314204845-cd278f2d3394
......@@ -71,6 +78,7 @@ require (
github.com/kubernetes-sigs/application v0.0.0-20190404151855-67ae7f915d4e
github.com/kubesphere/s2ioperator v0.0.12
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/lucas-clemente/quic-go v0.11.1 // indirect
github.com/mattn/go-sqlite3 v1.11.0 // indirect
......@@ -79,7 +87,7 @@ require (
github.com/miekg/dns v1.1.9 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
......@@ -87,11 +95,15 @@ require (
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.9.2 // indirect
github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba // indirect
github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef // indirect
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee // indirect
github.com/projectcalico/libcalico-go v0.0.0-20190708183129-ac36d966132f
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
github.com/prometheus/common v0.4.0 // indirect
github.com/prometheus/common v0.4.0
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
github.com/speps/go-hashids v2.0.0+incompatible
......@@ -105,12 +117,15 @@ require (
go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.10.0 // indirect
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
golang.org/x/tools/gopls v0.1.3 // indirect
google.golang.org/appengine v1.5.0 // indirect
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/igm/sockjs-go.v2 v2.0.0
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
......
此差异已折叠。
......@@ -18,6 +18,7 @@ set -o errexit
set -o nounset
set -o pipefail
GOPATH=`go env GOPATH`
# generate-groups generates everything for a project with external types only, e.g. a project based
# on CustomResourceDefinitions.
......
......@@ -40,6 +40,6 @@ BUILD_GOARCH=${GOARCH:-amd64}
GOBINARY=${GOBINARY:-go}
# forgoing -i (incremental build) because it will be deprecated by tool chain.
time GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} ${GOBINARY} build \
time GOOS=${BUILD_GOOS} CGO_ENABLED=0 GOARCH=${BUILD_GOARCH} ${GOBINARY} build \
-o ${OUT} \
${BUILDPATH}
approvers:
- magicsong
- zheng1
reviewers:
- magicsong
- zheng1
labels:
- area/deploy
- area/networking
\ No newline at end of file
此差异已折叠。
......@@ -6,9 +6,13 @@ metadata:
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: WorkspaceNetworkPolicy
plural: workspacenetworkpolicies
scope: "Cluster"
shortNames:
- wsnp
scope: Cluster
validation:
openAPIV3Schema:
description: WorkspaceNetworkPolicy is a set of network policies applied to
......
resources:
- network.yaml
- crds/wsnp.yaml
- crds/nsnp.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-90fa3885
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
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:90fa3885
name: manager
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
subjects:
- kind: ServiceAccount
name: default
namespace: network-test-90fa3885
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
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
\ No newline at end of file
package v1alpha1
import (
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
corev1 "k8s.io/api/core/v1"
)
// 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 kernel’s 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"
)
/*
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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// All types in this file is copy from calicoapi as we use calico to policy
// NamespaceNetworkPolicySpec defines the desired state of 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
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies API
// +k8s:openapi-gen=true
// +kubebuilder:resource:categories="networking",shortName="nsnp"
type NamespaceNetworkPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec NamespaceNetworkPolicySpec `json:"spec,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// NamespaceNetworkPolicyList contains a list of NamespaceNetworkPolicy
type NamespaceNetworkPolicyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []NamespaceNetworkPolicy `json:"items"`
}
func init() {
SchemeBuilder.Register(&NamespaceNetworkPolicy{}, &NamespaceNetworkPolicyList{})
}
/*
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 TestStorageNamespaceNetworkPolicy(t *testing.T) {
key := types.NamespacedName{
Name: "foo",
Namespace: "default",
}
created := &NamespaceNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
}}
g := gomega.NewGomegaWithT(t)
// Test Create
fetched := &NamespaceNetworkPolicy{}
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())
}
// 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)
}
// 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
// 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")
}
// 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
}
// 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)
}
}
// 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
}
}
// 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.
)
// 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
}
......@@ -33,7 +33,7 @@ var c client.Client
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "kustomize", "network", "crds")},
}
err := SchemeBuilder.AddToScheme(scheme.Scheme)
......
......@@ -75,6 +75,7 @@ type WorkspaceNetworkPolicyStatus struct {
// 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"`
......
......@@ -16,16 +16,287 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// autogenerated by controller-gen object, do not modify manually
package v1alpha1
import (
v1 "k8s.io/api/networking/v1"
"k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicy.
func (in *NamespaceNetworkPolicy) DeepCopy() *NamespaceNetworkPolicy {
if in == nil {
return nil
}
out := new(NamespaceNetworkPolicy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NamespaceNetworkPolicy) 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 *NamespaceNetworkPolicyList) DeepCopyInto(out *NamespaceNetworkPolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]NamespaceNetworkPolicy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicyList.
func (in *NamespaceNetworkPolicyList) DeepCopy() *NamespaceNetworkPolicyList {
if in == nil {
return nil
}
out := new(NamespaceNetworkPolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NamespaceNetworkPolicyList) 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 *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))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Egress != nil {
in, out := &in.Egress, &out.Egress
*out = make([]Rule, 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))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicySpec.
func (in *NamespaceNetworkPolicySpec) DeepCopy() *NamespaceNetworkPolicySpec {
if in == nil {
return nil
}
out := new(NamespaceNetworkPolicySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
*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)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
func (in *Rule) DeepCopy() *Rule {
if in == nil {
return nil
}
out := new(Rule)
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
......@@ -33,7 +304,6 @@ func (in *WorkspaceNetworkPolicy) DeepCopyInto(out *WorkspaceNetworkPolicy) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicy.
......@@ -59,7 +329,7 @@ func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPo
*out = *in
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]v1.NetworkPolicyPort, len(*in))
*out = make([]networkingv1.NetworkPolicyPort, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
......@@ -71,7 +341,6 @@ func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPo
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyEgressRule.
......@@ -89,7 +358,7 @@ func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkP
*out = *in
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]v1.NetworkPolicyPort, len(*in))
*out = make([]networkingv1.NetworkPolicyPort, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
......@@ -101,7 +370,6 @@ func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkP
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyIngressRule.
......@@ -126,7 +394,6 @@ func (in *WorkspaceNetworkPolicyList) DeepCopyInto(out *WorkspaceNetworkPolicyLi
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyList.
......@@ -156,7 +423,6 @@ func (in *WorkspaceNetworkPolicyPeer) DeepCopyInto(out *WorkspaceNetworkPolicyPe
*out = new(metav1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyPeer.
......@@ -174,7 +440,7 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySp
*out = *in
if in.PolicyTypes != nil {
in, out := &in.PolicyTypes, &out.PolicyTypes
*out = make([]v1.PolicyType, len(*in))
*out = make([]networkingv1.PolicyType, len(*in))
copy(*out, *in)
}
if in.Ingress != nil {
......@@ -191,7 +457,6 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySp
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicySpec.
......@@ -207,7 +472,6 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopy() *WorkspaceNetworkPolicySpec {
// 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
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyStatus.
......
/*
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"
)
// FakeNamespaceNetworkPolicies implements NamespaceNetworkPolicyInterface
type FakeNamespaceNetworkPolicies struct {
Fake *FakeNetworkV1alpha1
ns string
}
var namespacenetworkpoliciesResource = schema.GroupVersionResource{Group: "network.kubesphere.io", Version: "v1alpha1", Resource: "namespacenetworkpolicies"}
var namespacenetworkpoliciesKind = schema.GroupVersionKind{Group: "network.kubesphere.io", Version: "v1alpha1", Kind: "NamespaceNetworkPolicy"}
// Get takes name of the namespaceNetworkPolicy, and returns the corresponding namespaceNetworkPolicy object, and an error if there is any.
func (c *FakeNamespaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(namespacenetworkpoliciesResource, c.ns, name), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
// List takes label and field selectors, and returns the list of NamespaceNetworkPolicies that match those selectors.
func (c *FakeNamespaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.NamespaceNetworkPolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(namespacenetworkpoliciesResource, namespacenetworkpoliciesKind, c.ns, opts), &v1alpha1.NamespaceNetworkPolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.NamespaceNetworkPolicyList{ListMeta: obj.(*v1alpha1.NamespaceNetworkPolicyList).ListMeta}
for _, item := range obj.(*v1alpha1.NamespaceNetworkPolicyList).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 namespaceNetworkPolicies.
func (c *FakeNamespaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(namespacenetworkpoliciesResource, c.ns, opts))
}
// Create takes the representation of a namespaceNetworkPolicy and creates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *FakeNamespaceNetworkPolicies) Create(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(namespacenetworkpoliciesResource, c.ns, namespaceNetworkPolicy), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
// Update takes the representation of a namespaceNetworkPolicy and updates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *FakeNamespaceNetworkPolicies) Update(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(namespacenetworkpoliciesResource, c.ns, namespaceNetworkPolicy), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
// Delete takes name of the namespaceNetworkPolicy and deletes it. Returns an error if one occurs.
func (c *FakeNamespaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(namespacenetworkpoliciesResource, c.ns, name), &v1alpha1.NamespaceNetworkPolicy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeNamespaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(namespacenetworkpoliciesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.NamespaceNetworkPolicyList{})
return err
}
// Patch applies the patch and returns the patched namespaceNetworkPolicy.
func (c *FakeNamespaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(namespacenetworkpoliciesResource, c.ns, name, pt, data, subresources...), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
......@@ -28,6 +28,10 @@ type FakeNetworkV1alpha1 struct {
*testing.Fake
}
func (c *FakeNetworkV1alpha1) NamespaceNetworkPolicies(namespace string) v1alpha1.NamespaceNetworkPolicyInterface {
return &FakeNamespaceNetworkPolicies{c, namespace}
}
func (c *FakeNetworkV1alpha1) WorkspaceNetworkPolicies() v1alpha1.WorkspaceNetworkPolicyInterface {
return &FakeWorkspaceNetworkPolicies{c}
}
......
......@@ -18,4 +18,6 @@ limitations under the License.
package v1alpha1
type NamespaceNetworkPolicyExpansion interface{}
type WorkspaceNetworkPolicyExpansion interface{}
/*
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"
)
// NamespaceNetworkPoliciesGetter has a method to return a NamespaceNetworkPolicyInterface.
// A group's client should implement this interface.
type NamespaceNetworkPoliciesGetter interface {
NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyInterface
}
// NamespaceNetworkPolicyInterface has methods to work with NamespaceNetworkPolicy resources.
type NamespaceNetworkPolicyInterface interface {
Create(*v1alpha1.NamespaceNetworkPolicy) (*v1alpha1.NamespaceNetworkPolicy, error)
Update(*v1alpha1.NamespaceNetworkPolicy) (*v1alpha1.NamespaceNetworkPolicy, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.NamespaceNetworkPolicy, error)
List(opts v1.ListOptions) (*v1alpha1.NamespaceNetworkPolicyList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.NamespaceNetworkPolicy, err error)
NamespaceNetworkPolicyExpansion
}
// namespaceNetworkPolicies implements NamespaceNetworkPolicyInterface
type namespaceNetworkPolicies struct {
client rest.Interface
ns string
}
// newNamespaceNetworkPolicies returns a NamespaceNetworkPolicies
func newNamespaceNetworkPolicies(c *NetworkV1alpha1Client, namespace string) *namespaceNetworkPolicies {
return &namespaceNetworkPolicies{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the namespaceNetworkPolicy, and returns the corresponding namespaceNetworkPolicy object, and an error if there is any.
func (c *namespaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Get().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of NamespaceNetworkPolicies that match those selectors.
func (c *namespaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.NamespaceNetworkPolicyList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.NamespaceNetworkPolicyList{}
err = c.client.Get().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested namespaceNetworkPolicies.
func (c *namespaceNetworkPolicies) 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().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a namespaceNetworkPolicy and creates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *namespaceNetworkPolicies) Create(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Post().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Body(namespaceNetworkPolicy).
Do().
Into(result)
return
}
// Update takes the representation of a namespaceNetworkPolicy and updates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *namespaceNetworkPolicies) Update(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Put().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Name(namespaceNetworkPolicy.Name).
Body(namespaceNetworkPolicy).
Do().
Into(result)
return
}
// Delete takes name of the namespaceNetworkPolicy and deletes it. Returns an error if one occurs.
func (c *namespaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *namespaceNetworkPolicies) 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().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched namespaceNetworkPolicy.
func (c *namespaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("namespacenetworkpolicies").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}
......@@ -27,6 +27,7 @@ import (
type NetworkV1alpha1Interface interface {
RESTClient() rest.Interface
NamespaceNetworkPoliciesGetter
WorkspaceNetworkPoliciesGetter
}
......@@ -35,6 +36,10 @@ type NetworkV1alpha1Client struct {
restClient rest.Interface
}
func (c *NetworkV1alpha1Client) NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyInterface {
return newNamespaceNetworkPolicies(c, namespace)
}
func (c *NetworkV1alpha1Client) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInterface {
return newWorkspaceNetworkPolicies(c)
}
......
......@@ -60,6 +60,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBinaries().Informer()}, nil
// 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
......
......@@ -24,6 +24,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// NamespaceNetworkPolicies returns a NamespaceNetworkPolicyInformer.
NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer
// WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer.
WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer
}
......@@ -39,6 +41,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// NamespaceNetworkPolicies returns a NamespaceNetworkPolicyInformer.
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}
......
/*
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"
)
// NamespaceNetworkPolicyInformer provides access to a shared informer and lister for
// NamespaceNetworkPolicies.
type NamespaceNetworkPolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.NamespaceNetworkPolicyLister
}
type namespaceNetworkPolicyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewNamespaceNetworkPolicyInformer constructs a new informer for NamespaceNetworkPolicy 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 NewNamespaceNetworkPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredNamespaceNetworkPolicyInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredNamespaceNetworkPolicyInformer constructs a new informer for NamespaceNetworkPolicy 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 NewFilteredNamespaceNetworkPolicyInformer(client versioned.Interface, namespace string, 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().NamespaceNetworkPolicies(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkV1alpha1().NamespaceNetworkPolicies(namespace).Watch(options)
},
},
&networkv1alpha1.NamespaceNetworkPolicy{},
resyncPeriod,
indexers,
)
}
func (f *namespaceNetworkPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredNamespaceNetworkPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *namespaceNetworkPolicyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&networkv1alpha1.NamespaceNetworkPolicy{}, f.defaultInformer)
}
func (f *namespaceNetworkPolicyInformer) Lister() v1alpha1.NamespaceNetworkPolicyLister {
return v1alpha1.NewNamespaceNetworkPolicyLister(f.Informer().GetIndexer())
}
......@@ -18,6 +18,14 @@ limitations under the License.
package v1alpha1
// NamespaceNetworkPolicyListerExpansion allows custom methods to be added to
// NamespaceNetworkPolicyLister.
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{}
/*
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"
)
// NamespaceNetworkPolicyLister helps list NamespaceNetworkPolicies.
type NamespaceNetworkPolicyLister interface {
// List lists all NamespaceNetworkPolicies in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error)
// NamespaceNetworkPolicies returns an object that can list and get NamespaceNetworkPolicies.
NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyNamespaceLister
NamespaceNetworkPolicyListerExpansion
}
// namespaceNetworkPolicyLister implements the NamespaceNetworkPolicyLister interface.
type namespaceNetworkPolicyLister struct {
indexer cache.Indexer
}
// NewNamespaceNetworkPolicyLister returns a new NamespaceNetworkPolicyLister.
func NewNamespaceNetworkPolicyLister(indexer cache.Indexer) NamespaceNetworkPolicyLister {
return &namespaceNetworkPolicyLister{indexer: indexer}
}
// List lists all NamespaceNetworkPolicies in the indexer.
func (s *namespaceNetworkPolicyLister) List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.NamespaceNetworkPolicy))
})
return ret, err
}
// NamespaceNetworkPolicies returns an object that can list and get NamespaceNetworkPolicies.
func (s *namespaceNetworkPolicyLister) NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyNamespaceLister {
return namespaceNetworkPolicyNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// NamespaceNetworkPolicyNamespaceLister helps list and get NamespaceNetworkPolicies.
type NamespaceNetworkPolicyNamespaceLister interface {
// List lists all NamespaceNetworkPolicies in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error)
// Get retrieves the NamespaceNetworkPolicy from the indexer for a given namespace and name.
Get(name string) (*v1alpha1.NamespaceNetworkPolicy, error)
NamespaceNetworkPolicyNamespaceListerExpansion
}
// namespaceNetworkPolicyNamespaceLister implements the NamespaceNetworkPolicyNamespaceLister
// interface.
type namespaceNetworkPolicyNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all NamespaceNetworkPolicies in the indexer for a given namespace.
func (s namespaceNetworkPolicyNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.NamespaceNetworkPolicy))
})
return ret, err
}
// Get retrieves the NamespaceNetworkPolicy from the indexer for a given namespace and name.
func (s namespaceNetworkPolicyNamespaceLister) Get(name string) (*v1alpha1.NamespaceNetworkPolicy, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("namespacenetworkpolicy"), name)
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), nil
}
package controllerapi
// Controller expose Run method
type Controller interface {
Run(threadiness int, stopCh <-chan struct{}) error
}
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=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;create;update;patch;delete
package nsnetworkpolicy
import (
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"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/controller/network/provider"
)
const controllerAgentName = "nsnp-controller"
type controller struct {
kubeClientset kubernetes.Interface
kubesphereClientset kubesphereclient.Interface
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
}
var (
log = klogr.New().WithName("Controller").WithValues("Component", controllerAgentName)
errCount = 0
)
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,
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
}
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")
}
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.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 *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", key)
return nil
}(obj)
if err != nil {
utilruntime.HandleError(err)
return true
}
return true
}
package nsnetworkpolicy
import (
"flag"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/klog"
)
func TestNsnetworkpolicy(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, "Nsnetworkpolicy Suite")
}
package nsnetworkpolicy
import (
. "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"
"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
)
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()
})
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)
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(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())
})
AfterEach(func() {
close(stopCh)
})
})
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)
}
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
}
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"
)
// 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
}
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
}
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"
)
type CalicoDataStoreType string
const (
certPath = "/calicocerts"
KubernetesDataStore CalicoDataStoreType = "k8s"
EtcdDataStore CalicoDataStoreType = "etcd"
)
type RunOption struct {
ProviderName string
DataStoreType CalicoDataStoreType
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
}
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
}
......@@ -29,6 +29,7 @@ import (
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"
......@@ -38,10 +39,6 @@ var (
errCount = 0
)
// Controller expose Run method
type Controller interface {
Run(threadiness int, stopCh <-chan struct{}) error
}
type controller struct {
kubeClientset kubernetes.Interface
kubesphereClientset kubesphereclient.Interface
......@@ -77,7 +74,7 @@ func NewController(kubeclientset kubernetes.Interface,
wsnpInformer networkinformer.WorkspaceNetworkPolicyInformer,
networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer,
namespaceInformer corev1informer.NamespaceInformer,
workspaceInformer workspaceinformer.WorkspaceInformer) Controller {
workspaceInformer workspaceinformer.WorkspaceInformer) controllerapi.Controller {
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
log.V(4).Info("Creating event broadcaster")
eventBroadcaster := record.NewBroadcaster()
......
......@@ -16,12 +16,13 @@ import (
"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 Controller
c controllerapi.Controller
npLister netv1lister.NetworkPolicyLister
stopCh chan struct{}
deletePolicy metav1.DeletionPropagation
......
......@@ -467,6 +467,8 @@ func GetUserInfo(username string) (*models.User, error) {
return nil, err
}
defer conn.Close()
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
......@@ -1162,6 +1164,8 @@ func DescribeGroup(path string) (*models.Group, error) {
return nil, err
}
defer conn.Close()
groupSearchRequest := ldap.NewSearchRequest(searchBase,
ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=posixGroup)(cn=%s))", cn),
......
// Copyright 2018 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 test
import (
"context"
"fmt"
"io"
"io/ioutil"
"strconv"
"strings"
"testing"
"time"
"k8s.io/klog"
"github.com/prometheus/common/log"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
extscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
type TestCtx struct {
Client client.Client
ID string
t *testing.T
cleanupFns []cleanupFn
Namespace string
}
type CleanupOptions struct {
TestContext *TestCtx
Timeout time.Duration
RetryInterval time.Duration
}
type cleanupFn func(option *CleanupOptions) error
type AddToSchemeFunc = func(*runtime.Scheme) error
func NewTestCtx(t *testing.T, namespace string) *TestCtx {
var prefix string
if t != nil {
// TestCtx is used among others for namespace names where '/' is forbidden
prefix = strings.TrimPrefix(
strings.Replace(
strings.ToLower(t.Name()),
"/",
"-",
-1,
),
"test",
)
} else {
prefix = "main"
}
id := prefix + "-" + strconv.FormatInt(time.Now().Unix(), 10)
return &TestCtx{
ID: id,
t: t,
Namespace: namespace,
}
}
func (t *TestCtx) Setup(yamlPath string, crdPath string, schemes ...AddToSchemeFunc) error {
cfg, err := config.GetConfig()
if err != nil {
klog.Error("Failed to get kubeconfig")
return err
}
for _, f := range schemes {
f(scheme.Scheme)
}
extscheme.AddToScheme(scheme.Scheme)
dynClient, err := client.New(cfg, client.Options{})
if err != nil {
return err
}
t.Client = dynClient
err = EnsureNamespace(t.Client, t.Namespace)
if err != nil {
return err
}
bytes, err := ioutil.ReadFile(yamlPath)
if err != nil {
klog.Errorln("Failed to read yaml file")
return err
}
err = t.CreateFromYAML(bytes, true)
if err != nil {
klog.Error("Failed to install controller")
return err
}
return nil
}
func WaitForController(c client.Client, namespace, name string, replica int32, retryInterval, timeout time.Duration) error {
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) {
controller := &appsv1.Deployment{}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, controller)
if apierrors.IsNotFound(err) {
klog.Infof("Cannot find controller %s", name)
return false, nil
}
if err != nil {
klog.Errorf("Get error %s when waiting for controller up", err.Error())
return false, err
}
if controller.Status.ReadyReplicas == replica {
return true, nil
}
return false, nil
})
return err
}
func WaitForDeletion(dynclient client.Client, obj runtime.Object, retryInterval, timeout time.Duration) error {
key, err := client.ObjectKeyFromObject(obj)
if err != nil {
return err
}
kind := obj.GetObjectKind().GroupVersionKind().Kind
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = dynclient.Get(ctx, key, obj)
if apierrors.IsNotFound(err) {
return true, nil
}
if err != nil {
klog.Errorf("Get error %s when waiting for controller down", err.Error())
return false, err
}
klog.Infof("Waiting for %s %s to be deleted\n", kind, key)
return false, nil
})
if err != nil {
return err
}
return nil
}
func GetLogOfPod(rest *rest.RESTClient, namespace, name string, logOptions *corev1.PodLogOptions, out io.Writer) error {
req := rest.Get().Namespace(namespace).Name(name).SubResource("log").Param("follow", strconv.FormatBool(logOptions.Follow)).
Param("container", logOptions.Container).
Param("previous", strconv.FormatBool(logOptions.Previous)).
Param("timestamps", strconv.FormatBool(logOptions.Timestamps))
if logOptions.SinceSeconds != nil {
req.Param("sinceSeconds", strconv.FormatInt(*logOptions.SinceSeconds, 10))
}
if logOptions.SinceTime != nil {
req.Param("sinceTime", logOptions.SinceTime.Format(time.RFC3339))
}
if logOptions.LimitBytes != nil {
req.Param("limitBytes", strconv.FormatInt(*logOptions.LimitBytes, 10))
}
if logOptions.TailLines != nil {
req.Param("tailLines", strconv.FormatInt(*logOptions.TailLines, 10))
}
readCloser, err := req.Stream()
if err != nil {
return err
}
defer readCloser.Close()
_, err = io.Copy(out, readCloser)
return err
}
func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool) error {
decode := scheme.Codecs.UniversalDeserializer().Decode
scanner := NewYAMLScanner(yamlFile)
for scanner.Scan() {
yamlSpec := scanner.Bytes()
obj, groupVersionKind, err := decode(yamlSpec, nil, nil)
if err != nil {
klog.Errorf("Error while decoding YAML object. Err was: %s", err)
return err
}
klog.Infof("Successfully decode object %v", groupVersionKind)
err = ctx.Client.Create(context.TODO(), obj)
if skipIfExists && apierrors.IsAlreadyExists(err) {
continue
}
if err != nil {
klog.Errorf("Failed to create %v to k8s", obj)
return err
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to scan manifest: (%v)", err)
}
return nil
}
func (ctx *TestCtx) GetID() string {
return ctx.ID
}
func (ctx *TestCtx) Cleanup(option *CleanupOptions) {
failed := false
for i := len(ctx.cleanupFns) - 1; i >= 0; i-- {
err := ctx.cleanupFns[i](option)
if err != nil {
failed = true
if ctx.t != nil {
ctx.t.Errorf("A cleanup function failed with error: (%v)\n", err)
} else {
log.Errorf("A cleanup function failed with error: (%v)", err)
}
}
}
if ctx.t == nil && failed {
log.Fatal("A cleanup function failed")
}
}
func (ctx *TestCtx) AddCleanupFn(fn cleanupFn) {
ctx.cleanupFns = append(ctx.cleanupFns, fn)
}
func WaitForJobSucceed(c client.Client, namespace, name string, retryInterval, timeout time.Duration) error {
return waitForJobStatus(c, namespace, name, batchv1.JobComplete, retryInterval, timeout)
}
func WaitForJobFail(c client.Client, namespace, name string, retryInterval, timeout time.Duration) error {
return waitForJobStatus(c, namespace, name, batchv1.JobFailed, retryInterval, timeout)
}
func waitForJobStatus(c client.Client, namespace, name string, jobstatus batchv1.JobConditionType, retryInterval, timeout time.Duration) error {
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) {
job := &batchv1.Job{}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, job)
if apierrors.IsNotFound(err) {
klog.Infof("Cannot find job %s", name)
return false, nil
}
if err != nil {
klog.Errorf("Get error %s when waiting for job up", err.Error())
return false, err
}
if len(job.Status.Conditions) > 0 && job.Status.Conditions[0].Type == jobstatus {
return true, nil
}
return false, nil
})
return err
}
func EnsureNamespace(c client.Client, namespace string) error {
ns := &corev1.Namespace{}
ns.Name = namespace
err := c.Create(context.TODO(), ns)
if err != nil {
if apierrors.IsAlreadyExists(err) {
klog.Infof("Namespace %s is existed", namespace)
return nil
}
}
return err
}
func DeleteNamespace(c client.Client, namespace string) error {
ns := &corev1.Namespace{}
ns.Name = namespace
return c.Delete(context.TODO(), ns)
}
package test
import (
"bufio"
"bytes"
"io"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
)
const maxExecutiveEmpties = 100
// Scanner scans a yaml manifest file for manifest tokens delimited by "---".
// See bufio.Scanner for semantics.
type Scanner struct {
reader *k8syaml.YAMLReader
token []byte // Last token returned by split.
err error // Sticky error.
empties int // Count of successive empty tokens.
done bool // Scan has finished.
}
func NewYAMLScanner(b []byte) *Scanner {
r := bufio.NewReader(bytes.NewBuffer(b))
return &Scanner{reader: k8syaml.NewYAMLReader(r)}
}
func (s *Scanner) Err() error {
if s.err == io.EOF {
return nil
}
return s.err
}
func (s *Scanner) Scan() bool {
if s.done {
return false
}
var (
tok []byte
err error
)
for {
tok, err = s.reader.Read()
if err != nil {
if err == io.EOF {
s.done = true
}
s.err = err
return false
}
if len(bytes.TrimSpace(tok)) == 0 {
s.empties++
if s.empties > maxExecutiveEmpties {
panic("yaml.Scan: too many empty tokens without progressing")
}
continue
}
s.empties = 0
s.token = tok
return true
}
}
func (s *Scanner) Text() string {
return string(s.token)
}
func (s *Scanner) Bytes() []byte {
return s.token
}
package e2e_test
import (
"flag"
"os"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/test"
)
var ctx *test.TestCtx
func TestE2e(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Networking E2e Suite")
}
var _ = BeforeSuite(func() {
klog.InitFlags(nil)
flag.Set("logtostderr", "false")
flag.Set("alsologtostderr", "false")
flag.Set("v", "4")
flag.Parse()
klog.SetOutput(GinkgoWriter)
ctx = test.NewTestCtx(nil, os.Getenv("TEST_NAMESPACE"))
Expect(ctx.Setup(os.Getenv("YAML_PATH"), "", v1alpha1.AddToScheme)).ShouldNot(HaveOccurred())
deployName := os.Getenv("DEPLOY_NAME")
Expect(test.WaitForController(ctx.Client, ctx.Namespace, deployName, 1, time.Second*5, time.Minute)).ShouldNot(HaveOccurred(), "Controlller failed to start")
klog.Infoln("Controller is up, begin to test ")
})
var _ = AfterSuite(func() {
ctx.Cleanup(nil)
})
package e2e_test
import (
"context"
"time"
"k8s.io/klog"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes/scheme"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/test"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var simpleDeployYaml = `apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: production
labels:
name: nginx
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
name: nginx
app: nginx
color : red
spec:
containers:
- image: nginx:alpine
name: nginx
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "20m"
memory: "55M"
env:
- name: ENVVARNAME
value: ENVVARVALUE
ports:
- containerPort: 80
name: http
restartPolicy: Always`
var simpleNPYaml = `apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: allow-icmp-only
namespace: production
spec:
selector: color == 'red'
ingress:
- action: Allow
protocol: ICMP
source:
selector: color == 'blue'
namespaceSelector: all()`
var simpleJobYaml = `apiVersion: batch/v1
kind: Job
metadata:
name: test-connect
namespace: production
spec:
template:
metadata:
labels:
color : blue
spec:
containers:
- name: test-connect
image: alpine
command: ["ping", "1.1.1.1"]
restartPolicy: Never
backoffLimit: 1`
var testNs = "production"
var _ = Describe("E2e for network policy", func() {
BeforeEach(func() {
Expect(test.EnsureNamespace(ctx.Client, testNs)).ShouldNot(HaveOccurred())
})
AfterEach(func() {
Expect(test.DeleteNamespace(ctx.Client, testNs)).ShouldNot(HaveOccurred())
ns := &corev1.Namespace{}
ns.Name = testNs
Expect(test.WaitForDeletion(ctx.Client, ns, time.Second*5, time.Minute)).ShouldNot(HaveOccurred())
})
It("Should work well in simple namespaceNetworkPolicy", func() {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(simpleDeployYaml), nil, nil)
Expect(err).ShouldNot(HaveOccurred(), "Failed to parse yaml")
deploy := obj.(*appsv1.Deployment)
Expect(ctx.Client.Create(context.TODO(), obj)).ShouldNot(HaveOccurred())
Expect(test.WaitForController(ctx.Client, deploy.Namespace, deploy.Name, *deploy.Spec.Replicas, time.Second*2, time.Minute)).ShouldNot(HaveOccurred())
defer func() {
Expect(ctx.Client.Delete(context.TODO(), deploy)).ShouldNot(HaveOccurred())
}()
obj, _, err = decode([]byte(simpleNPYaml), nil, nil)
Expect(err).ShouldNot(HaveOccurred(), "Failed to parse networkpolicy yaml")
np := obj.(*v1alpha1.NamespaceNetworkPolicy)
Expect(ctx.Client.Create(context.TODO(), np)).ShouldNot(HaveOccurred())
defer func() {
Expect(ctx.Client.Delete(context.TODO(), np)).ShouldNot(HaveOccurred())
Expect(test.WaitForDeletion(ctx.Client, np, time.Second*2, time.Minute)).ShouldNot(HaveOccurred())
}()
obj, _, err = decode([]byte(simpleJobYaml), nil, nil)
Expect(err).ShouldNot(HaveOccurred(), "Failed to parse job yaml")
//create a job to test
job := obj.(*batchv1.Job)
selector, _ := labels.Parse("app=nginx")
podlist := &corev1.PodList{}
Expect(ctx.Client.List(context.TODO(), &client.ListOptions{
Namespace: deploy.Namespace,
LabelSelector: selector,
}, podlist)).ShouldNot(HaveOccurred())
Expect(podlist.Items).To(HaveLen(int(*deploy.Spec.Replicas)))
podip := podlist.Items[0].Status.PodIP
job.Spec.Template.Spec.Containers[0].Command = []string{"ping", "-c", "4", podip}
job.Spec.Template.Labels["color"] = "yellow"
orginalJob := job.DeepCopy()
Expect(ctx.Client.Create(context.TODO(), job)).ShouldNot(HaveOccurred())
defer func() {
Expect(ctx.Client.Delete(context.TODO(), job)).ShouldNot(HaveOccurred())
}()
klog.Infoln("sleep 10s to wait for controller creating np")
time.Sleep(time.Second * 10)
Expect(test.WaitForJobFail(ctx.Client, job.Namespace, job.Name, time.Second*3, time.Minute)).ShouldNot(HaveOccurred(), "Failed to block connection")
//change job color
job = orginalJob.DeepCopy()
Expect(ctx.Client.Delete(context.TODO(), job)).ShouldNot(HaveOccurred())
Expect(test.WaitForDeletion(ctx.Client, job, time.Second*2, time.Minute)).ShouldNot(HaveOccurred())
job.Spec.Template.Labels["color"] = "blue"
Expect(ctx.Client.Create(context.TODO(), job)).ShouldNot(HaveOccurred())
Expect(test.WaitForJobSucceed(ctx.Client, job.Namespace, job.Name, time.Second*3, time.Minute)).ShouldNot(HaveOccurred(), "Connection failed")
})
})
FROM golang:1.12
RUN apt-get update && apt-get install -y apt-transport-https jq openssl libltdl7 && \
go get -u github.com/onsi/ginkgo/ginkgo && \
curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest |\
grep browser_download |\
grep linux |\
cut -d '"' -f 4 |\
xargs curl -O -L && \
mv kustomize_*_linux_amd64 kustomize && \
chmod u+x kustomize && \
mv kustomize /usr/bin/
pipeline {
agent {
docker {
image 'magicsong/nete2e:v0.0.1'
args '-v gomod:/go -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker'
}
}
environment {
KUBECONFIG = "/root/.kube/config"
}
stages {
stage('set kubeconfig and secret'){
steps{
sh 'mkdir -p ~/.kube'
sh 'mkdir ./kustomize/network/etcd'
withCredentials([kubeconfigContent(credentialsId: 'net-kubeconfig', variable: 'KUBECONFIG_CONTENT'),
file(credentialsId: 'etcd-ca', variable: 'etcd_ca'),
file(credentialsId: 'etcd-crt', variable: 'etcd_crt'),
file(credentialsId: 'etcd-key', variable: 'etcd_key')]) {
sh 'echo "$KUBECONFIG_CONTENT" > ~/.kube/config'
sh "cp \${etcd_ca} ./kustomize/network/etcd/ca"
sh "cp \${etcd_key} ./kustomize/network/etcd/key"
sh "cp \${etcd_crt} ./kustomize/network/etcd/crt"
}
}
}
stage('testing') {
steps {
sh """
make network-e2e
"""
}
}
stage('cleanup'){
steps{
sh """
rm -rf ./kustomize/network/etcd
"""
}
}
}
}
\ No newline at end of file
approvers:
- magicsong
- zheng1
reviewers:
- magicsong
- zheng1
labels:
- area/testing
- area/networking
\ No newline at end of file
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: allow-icmp-only
namespace: production
spec:
selector: color == 'red'
ingress:
- action: Allow
protocol: ICMP
source:
selector: "all()"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: production
labels:
name: nginx
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
name: nginx
app: nginx
color : red
spec:
containers:
- image: nginx:alpine
name: nginx
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "20m"
memory: "55M"
env:
- name: ENVVARNAME
value: ENVVARVALUE
ports:
- containerPort: 80
name: http
restartPolicy: Always
\ No newline at end of file
apiVersion: batch/v1
kind: Job
metadata:
name: test-connect
namespace: production
spec:
template:
metadata:
labels:
color : blue
spec:
containers:
- name: test-connect
image: alpine
command: ["ping", "10.17.30.131"]
restartPolicy: Never
backoffLimit: 1
\ No newline at end of file
#!/bin/bash
set -e
workspace=`pwd`
tag=`git rev-parse --short HEAD`
IMG=magicsong/ks-network:$tag
DEST=/tmp/manager.yaml
TEST_NS=network-test-$tag
SKIP_BUILD=no
export TEST_NAMESPACE=$TEST_NS
export YAML_PATH=$DEST
export CRD_PATH=$workspace/kustomize/crds
export DEPLOY_NAME=network-manager
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-s|--skip-build)
SKIP_BUILD=yes
shift # past argument
;;
-n|--NAMESPACE)
TEST_NS=$2
shift # past argument
shift # past value
;;
-t|--tag)
tag="$2"
shift # past argument
shift # past value
;;
--default)
DEFAULT=YES
shift # past argument
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
if [ $SKIP_BUILD == "no" ]; then
echo "Building binary"
hack/gobuild.sh cmd/ks-network
docker build -f build/ks-network/Dockerfile -t $IMG bin/cmd
echo "Push images"
docker push $IMG
fi
kustomize_dir="./kustomize/network"
if [ "$(uname)" == "Darwin" ]; then
sed -i '' -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/kustomization.yaml
sed -i '' -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/patch_role_binding.yaml
sed -i '' -e 's@image: .*@image: '"${IMG}"'@' $kustomize_dir/patch_image_name.yaml
else
sed -i -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/patch_role_binding.yaml
sed -i -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/kustomization.yaml
sed -i -e 's@image: .*@image: '"${IMG}"'@' $kustomize_dir/patch_image_name.yaml
fi
kustomize build $kustomize_dir -o $DEST
ginkgo -v ./test/e2e/...
# This is the official list of cloud authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Filippo Valsorda <hi@filippo.io>
Google Inc.
Ingo Oeser <nightlyone@googlemail.com>
Palm Stone Games, Inc.
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Tyler Treat <ttreat31@gmail.com>
此差异已折叠。
此差异已折叠。
此差异已折叠。
module contrib.go.opencensus.io/exporter/ocagent
require (
github.com/census-instrumentation/opencensus-proto v0.2.0 // this is to match the version used in census-instrumentation/opencensus-service
github.com/golang/protobuf v1.3.1
github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
go.opencensus.io v0.20.2
google.golang.org/api v0.3.1
google.golang.org/grpc v1.19.1
)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册