service policy

refactor virtualservice controller
This commit is contained in:
Jeff
2019-03-28 14:02:51 +08:00
committed by zryfish
parent 43217d16a3
commit 2e1dc6a7b5
28 changed files with 2095 additions and 565 deletions

View File

@@ -64,7 +64,7 @@ deploy: manifests
# Generate DeepCopy to implement runtime.Object
deepcopy:
./vendor/k8s.io/code-generator/generate-groups.sh deepcopy kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "servicemesh:v1alpha2"
./vendor/k8s.io/code-generator/generate-groups.sh deepcopy,lister,informer,client kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "servicemesh:v1alpha2"
# Generate code
generate:

View File

@@ -56,6 +56,7 @@ func AddControllers(mgr manager.Manager, cfg *rest.Config, stopCh <-chan struct{
drController := destinationrule.NewDestinationRuleController(informerFactory.Apps().V1().Deployments(),
istioInformer.Networking().V1alpha3().DestinationRules(),
informerFactory.Core().V1().Services(),
servicemeshinformer.Servicemesh().V1alpha2().ServicePolicies(),
kubeClient,
istioclient)

View File

@@ -127,7 +127,7 @@ func initializeESClientConfig() {
err := db.Find(&outputs).Error
if err != nil {
log.Printf("get logging config failed. Error: %v", err)
log.Printf("get logging config failed. Error: %v", err)
return
}

View File

@@ -0,0 +1,822 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
labels:
controller-tools.k8s.io: "1.0"
name: servicepolicies.servicemesh.kubesphere.io
spec:
group: servicemesh.kubesphere.io
names:
kind: ServicePolicy
plural: servicepolicies
scope: Namespaced
validation:
openAPIV3Schema:
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
properties:
selector:
description: Label selector for destination rules.
type: object
template:
description: Template used to create a destination rule
properties:
spec:
description: Spec indicates the behavior of a destination rule.
properties:
host:
description: 'REQUIRED. The name of a service from the service
registry. Service names are looked up from the platform''s
service registry (e.g., Kubernetes services, Consul services,
etc.) and from the hosts declared by [ServiceEntries](#ServiceEntry).
Rules defined for services that do not exist in the service
registry will be ignored. *Note for Kubernetes users*: When
short names are used (e.g. "reviews" instead of "reviews.default.svc.cluster.local"),
Istio will interpret the short name based on the namespace
of the rule, not the service. A rule in the "default" namespace
containing a host "reviews will be interpreted as "reviews.default.svc.cluster.local",
irrespective of the actual namespace associated with the reviews
service. _To avoid potential misconfigurations, it is recommended
to always use fully qualified domain names over short names._ Note
that the host field applies to both HTTP and TCP services.'
type: string
subsets:
description: One or more named sets that represent individual
versions of a service. Traffic policies can be overridden
at subset level.
items:
properties:
labels:
description: REQUIRED. Labels apply a filter over the
endpoints of a service in the service registry. See
route rules for examples of usage.
type: object
name:
description: REQUIRED. Name of the subset. The service
name and the subset name can be used for traffic splitting
in a route rule.
type: string
trafficPolicy:
description: Traffic policies that apply to this subset.
Subsets inherit the traffic policies specified at the
DestinationRule level. Settings specified at the subset
level will override the corresponding settings specified
at the DestinationRule level.
properties:
connectionPool:
description: Settings controlling the volume of connections
to an upstream service
properties:
http:
description: HTTP connection pool settings.
properties:
maxRequestsPerConnection:
description: Maximum number of requests per
connection to a backend. Setting this parameter
to 1 disables keep alive.
format: int32
type: integer
maxRetries:
description: Maximum number of retries that
can be outstanding to all hosts in a cluster
at a given time. Defaults to 3.
format: int32
type: integer
type: object
tcp:
description: Settings common to both HTTP and
TCP upstream connections.
properties:
connectTimeout:
description: TCP connection timeout.
type: string
maxConnections:
description: Maximum number of HTTP1 /TCP
connections to a destination host.
format: int32
type: integer
type: object
type: object
loadBalancer:
description: Settings controlling the load balancer
algorithms.
properties:
consistentHash:
properties:
httpCookie:
description: Hash based on HTTP cookie.
properties:
name:
description: REQUIRED. Name of the cookie.
type: string
path:
description: Path to set for the cookie.
type: string
ttl:
description: REQUIRED. Lifetime of the
cookie.
type: string
required:
- name
- ttl
type: object
httpHeaderName:
description: 'It is required to specify exactly
one of the fields as hash key: HttpHeaderName,
HttpCookie, or UseSourceIP. Hash based on
a specific HTTP header.'
type: string
minimumRingSize:
description: The minimum number of virtual
nodes to use for the hash ring. Defaults
to 1024. Larger ring sizes result in more
granular load distributions. If the number
of hosts in the load balancing pool is larger
than the ring size, each host will be assigned
a single virtual node.
format: int64
type: integer
useSourceIp:
description: Hash based on the source IP address.
type: boolean
type: object
simple:
description: 'It is required to specify exactly
one of the fields: Simple or ConsistentHash'
type: string
type: object
outlierDetection:
description: Settings controlling eviction of unhealthy
hosts from the load balancing pool
properties:
baseEjectionTime:
description: 'Minimum ejection duration. A host
will remain ejected for a period equal to the
product of minimum ejection duration and the
number of times the host has been ejected. This
technique allows the system to automatically
increase the ejection period for unhealthy upstream
servers. format: 1h/1m/1s/1ms. MUST BE >=1ms.
Default is 30s.'
type: string
consecutiveErrors:
description: Number of errors before a host is
ejected from the connection pool. Defaults to
5. When the upstream host is accessed over HTTP,
a 5xx return code qualifies as an error. When
the upstream host is accessed over an opaque
TCP connection, connect timeouts and connection
error/failure events qualify as an error.
format: int32
type: integer
interval:
description: 'Time interval between ejection sweep
analysis. format: 1h/1m/1s/1ms. MUST BE >=1ms.
Default is 10s.'
type: string
maxEjectionPercent:
description: Maximum % of hosts in the load balancing
pool for the upstream service that can be ejected.
Defaults to 10%.
format: int32
type: integer
type: object
portLevelSettings:
description: Traffic policies specific to individual
ports. Note that port level settings will override
the destination-level settings. Traffic settings
specified at the destination-level will not be inherited
when overridden by port-level settings, i.e. default
values will be applied to fields omitted in port-level
traffic policies.
items:
properties:
connectionPool:
description: Settings controlling the volume
of connections to an upstream service
properties:
http:
description: HTTP connection pool settings.
properties:
maxRequestsPerConnection:
description: Maximum number of requests
per connection to a backend. Setting
this parameter to 1 disables keep
alive.
format: int32
type: integer
maxRetries:
description: Maximum number of retries
that can be outstanding to all hosts
in a cluster at a given time. Defaults
to 3.
format: int32
type: integer
type: object
tcp:
description: Settings common to both HTTP
and TCP upstream connections.
properties:
connectTimeout:
description: TCP connection timeout.
type: string
maxConnections:
description: Maximum number of HTTP1
/TCP connections to a destination
host.
format: int32
type: integer
type: object
type: object
loadBalancer:
description: Settings controlling the load balancer
algorithms.
properties:
consistentHash:
properties:
httpCookie:
description: Hash based on HTTP cookie.
properties:
name:
description: REQUIRED. Name of the
cookie.
type: string
path:
description: Path to set for the
cookie.
type: string
ttl:
description: REQUIRED. Lifetime
of the cookie.
type: string
required:
- name
- ttl
type: object
httpHeaderName:
description: 'It is required to specify
exactly one of the fields as hash
key: HttpHeaderName, HttpCookie, or
UseSourceIP. Hash based on a specific
HTTP header.'
type: string
minimumRingSize:
description: The minimum number of virtual
nodes to use for the hash ring. Defaults
to 1024. Larger ring sizes result
in more granular load distributions.
If the number of hosts in the load
balancing pool is larger than the
ring size, each host will be assigned
a single virtual node.
format: int64
type: integer
useSourceIp:
description: Hash based on the source
IP address.
type: boolean
type: object
simple:
description: 'It is required to specify
exactly one of the fields: Simple or ConsistentHash'
type: string
type: object
outlierDetection:
description: Settings controlling eviction of
unhealthy hosts from the load balancing pool
properties:
baseEjectionTime:
description: 'Minimum ejection duration.
A host will remain ejected for a period
equal to the product of minimum ejection
duration and the number of times the host
has been ejected. This technique allows
the system to automatically increase the
ejection period for unhealthy upstream
servers. format: 1h/1m/1s/1ms. MUST BE
>=1ms. Default is 30s.'
type: string
consecutiveErrors:
description: Number of errors before a host
is ejected from the connection pool. Defaults
to 5. When the upstream host is accessed
over HTTP, a 5xx return code qualifies
as an error. When the upstream host is
accessed over an opaque TCP connection,
connect timeouts and connection error/failure
events qualify as an error.
format: int32
type: integer
interval:
description: 'Time interval between ejection
sweep analysis. format: 1h/1m/1s/1ms.
MUST BE >=1ms. Default is 10s.'
type: string
maxEjectionPercent:
description: Maximum % of hosts in the load
balancing pool for the upstream service
that can be ejected. Defaults to 10%.
format: int32
type: integer
type: object
port:
description: Specifies the port name or number
of a port on the destination service on which
this policy is being applied. Names must
comply with DNS label syntax (rfc1035) and
therefore cannot collide with numbers. If
there are multiple ports on a service with
the same protocol the names should be of the
form <protocol-name>-<DNS label>.
properties:
name:
description: Valid port name
type: string
number:
description: Valid port number
format: int32
type: integer
type: object
tls:
description: TLS related settings for connections
to the upstream service.
properties:
caCertificates:
description: 'OPTIONAL: The path to the
file containing certificate authority
certificates to use in verifying a presented
server certificate. If omitted, the proxy
will not verify the server''s certificate.
Should be empty if mode is `ISTIO_MUTUAL`.'
type: string
clientCertificate:
description: REQUIRED if mode is `MUTUAL`.
The path to the file holding the client-side
TLS certificate to use. Should be empty
if mode is `ISTIO_MUTUAL`.
type: string
mode:
description: 'REQUIRED: Indicates whether
connections to this port should be secured
using TLS. The value of this field determines
how TLS is enforced.'
type: string
privateKey:
description: REQUIRED if mode is `MUTUAL`.
The path to the file holding the client's
private key. Should be empty if mode is
`ISTIO_MUTUAL`.
type: string
sni:
description: SNI string to present to the
server during TLS handshake. Should be
empty if mode is `ISTIO_MUTUAL`.
type: string
subjectAltNames:
description: A list of alternate names to
verify the subject identity in the certificate.
If specified, the proxy will verify that
the server certificate's subject alt name
matches one of the specified values. Should
be empty if mode is `ISTIO_MUTUAL`.
items:
type: string
type: array
required:
- mode
type: object
required:
- port
type: object
type: array
tls:
description: TLS related settings for connections
to the upstream service.
properties:
caCertificates:
description: 'OPTIONAL: The path to the file containing
certificate authority certificates to use in
verifying a presented server certificate. If
omitted, the proxy will not verify the server''s
certificate. Should be empty if mode is `ISTIO_MUTUAL`.'
type: string
clientCertificate:
description: REQUIRED if mode is `MUTUAL`. The
path to the file holding the client-side TLS
certificate to use. Should be empty if mode
is `ISTIO_MUTUAL`.
type: string
mode:
description: 'REQUIRED: Indicates whether connections
to this port should be secured using TLS. The
value of this field determines how TLS is enforced.'
type: string
privateKey:
description: REQUIRED if mode is `MUTUAL`. The
path to the file holding the client's private
key. Should be empty if mode is `ISTIO_MUTUAL`.
type: string
sni:
description: SNI string to present to the server
during TLS handshake. Should be empty if mode
is `ISTIO_MUTUAL`.
type: string
subjectAltNames:
description: A list of alternate names to verify
the subject identity in the certificate. If
specified, the proxy will verify that the server
certificate's subject alt name matches one of
the specified values. Should be empty if mode
is `ISTIO_MUTUAL`.
items:
type: string
type: array
required:
- mode
type: object
type: object
required:
- name
- labels
type: object
type: array
trafficPolicy:
description: Traffic policies to apply (load balancing policy,
connection pool sizes, outlier detection).
properties:
connectionPool:
description: Settings controlling the volume of connections
to an upstream service
properties:
http:
description: HTTP connection pool settings.
properties:
maxRequestsPerConnection:
description: Maximum number of requests per connection
to a backend. Setting this parameter to 1 disables
keep alive.
format: int32
type: integer
maxRetries:
description: Maximum number of retries that can
be outstanding to all hosts in a cluster at a
given time. Defaults to 3.
format: int32
type: integer
type: object
tcp:
description: Settings common to both HTTP and TCP upstream
connections.
properties:
connectTimeout:
description: TCP connection timeout.
type: string
maxConnections:
description: Maximum number of HTTP1 /TCP connections
to a destination host.
format: int32
type: integer
type: object
type: object
loadBalancer:
description: Settings controlling the load balancer algorithms.
properties:
consistentHash:
properties:
httpCookie:
description: Hash based on HTTP cookie.
properties:
name:
description: REQUIRED. Name of the cookie.
type: string
path:
description: Path to set for the cookie.
type: string
ttl:
description: REQUIRED. Lifetime of the cookie.
type: string
required:
- name
- ttl
type: object
httpHeaderName:
description: 'It is required to specify exactly
one of the fields as hash key: HttpHeaderName,
HttpCookie, or UseSourceIP. Hash based on a specific
HTTP header.'
type: string
minimumRingSize:
description: The minimum number of virtual nodes
to use for the hash ring. Defaults to 1024. Larger
ring sizes result in more granular load distributions.
If the number of hosts in the load balancing pool
is larger than the ring size, each host will be
assigned a single virtual node.
format: int64
type: integer
useSourceIp:
description: Hash based on the source IP address.
type: boolean
type: object
simple:
description: 'It is required to specify exactly one
of the fields: Simple or ConsistentHash'
type: string
type: object
outlierDetection:
description: Settings controlling eviction of unhealthy
hosts from the load balancing pool
properties:
baseEjectionTime:
description: 'Minimum ejection duration. A host will
remain ejected for a period equal to the product of
minimum ejection duration and the number of times
the host has been ejected. This technique allows the
system to automatically increase the ejection period
for unhealthy upstream servers. format: 1h/1m/1s/1ms.
MUST BE >=1ms. Default is 30s.'
type: string
consecutiveErrors:
description: Number of errors before a host is ejected
from the connection pool. Defaults to 5. When the
upstream host is accessed over HTTP, a 5xx return
code qualifies as an error. When the upstream host
is accessed over an opaque TCP connection, connect
timeouts and connection error/failure events qualify
as an error.
format: int32
type: integer
interval:
description: 'Time interval between ejection sweep analysis.
format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s.'
type: string
maxEjectionPercent:
description: Maximum % of hosts in the load balancing
pool for the upstream service that can be ejected.
Defaults to 10%.
format: int32
type: integer
type: object
portLevelSettings:
description: Traffic policies specific to individual ports.
Note that port level settings will override the destination-level
settings. Traffic settings specified at the destination-level
will not be inherited when overridden by port-level settings,
i.e. default values will be applied to fields omitted
in port-level traffic policies.
items:
properties:
connectionPool:
description: Settings controlling the volume of connections
to an upstream service
properties:
http:
description: HTTP connection pool settings.
properties:
maxRequestsPerConnection:
description: Maximum number of requests per
connection to a backend. Setting this parameter
to 1 disables keep alive.
format: int32
type: integer
maxRetries:
description: Maximum number of retries that
can be outstanding to all hosts in a cluster
at a given time. Defaults to 3.
format: int32
type: integer
type: object
tcp:
description: Settings common to both HTTP and
TCP upstream connections.
properties:
connectTimeout:
description: TCP connection timeout.
type: string
maxConnections:
description: Maximum number of HTTP1 /TCP
connections to a destination host.
format: int32
type: integer
type: object
type: object
loadBalancer:
description: Settings controlling the load balancer
algorithms.
properties:
consistentHash:
properties:
httpCookie:
description: Hash based on HTTP cookie.
properties:
name:
description: REQUIRED. Name of the cookie.
type: string
path:
description: Path to set for the cookie.
type: string
ttl:
description: REQUIRED. Lifetime of the
cookie.
type: string
required:
- name
- ttl
type: object
httpHeaderName:
description: 'It is required to specify exactly
one of the fields as hash key: HttpHeaderName,
HttpCookie, or UseSourceIP. Hash based on
a specific HTTP header.'
type: string
minimumRingSize:
description: The minimum number of virtual
nodes to use for the hash ring. Defaults
to 1024. Larger ring sizes result in more
granular load distributions. If the number
of hosts in the load balancing pool is larger
than the ring size, each host will be assigned
a single virtual node.
format: int64
type: integer
useSourceIp:
description: Hash based on the source IP address.
type: boolean
type: object
simple:
description: 'It is required to specify exactly
one of the fields: Simple or ConsistentHash'
type: string
type: object
outlierDetection:
description: Settings controlling eviction of unhealthy
hosts from the load balancing pool
properties:
baseEjectionTime:
description: 'Minimum ejection duration. A host
will remain ejected for a period equal to the
product of minimum ejection duration and the
number of times the host has been ejected. This
technique allows the system to automatically
increase the ejection period for unhealthy upstream
servers. format: 1h/1m/1s/1ms. MUST BE >=1ms.
Default is 30s.'
type: string
consecutiveErrors:
description: Number of errors before a host is
ejected from the connection pool. Defaults to
5. When the upstream host is accessed over HTTP,
a 5xx return code qualifies as an error. When
the upstream host is accessed over an opaque
TCP connection, connect timeouts and connection
error/failure events qualify as an error.
format: int32
type: integer
interval:
description: 'Time interval between ejection sweep
analysis. format: 1h/1m/1s/1ms. MUST BE >=1ms.
Default is 10s.'
type: string
maxEjectionPercent:
description: Maximum % of hosts in the load balancing
pool for the upstream service that can be ejected.
Defaults to 10%.
format: int32
type: integer
type: object
port:
description: Specifies the port name or number of
a port on the destination service on which this
policy is being applied. Names must comply with
DNS label syntax (rfc1035) and therefore cannot
collide with numbers. If there are multiple ports
on a service with the same protocol the names should
be of the form <protocol-name>-<DNS label>.
properties:
name:
description: Valid port name
type: string
number:
description: Valid port number
format: int32
type: integer
type: object
tls:
description: TLS related settings for connections
to the upstream service.
properties:
caCertificates:
description: 'OPTIONAL: The path to the file containing
certificate authority certificates to use in
verifying a presented server certificate. If
omitted, the proxy will not verify the server''s
certificate. Should be empty if mode is `ISTIO_MUTUAL`.'
type: string
clientCertificate:
description: REQUIRED if mode is `MUTUAL`. The
path to the file holding the client-side TLS
certificate to use. Should be empty if mode
is `ISTIO_MUTUAL`.
type: string
mode:
description: 'REQUIRED: Indicates whether connections
to this port should be secured using TLS. The
value of this field determines how TLS is enforced.'
type: string
privateKey:
description: REQUIRED if mode is `MUTUAL`. The
path to the file holding the client's private
key. Should be empty if mode is `ISTIO_MUTUAL`.
type: string
sni:
description: SNI string to present to the server
during TLS handshake. Should be empty if mode
is `ISTIO_MUTUAL`.
type: string
subjectAltNames:
description: A list of alternate names to verify
the subject identity in the certificate. If
specified, the proxy will verify that the server
certificate's subject alt name matches one of
the specified values. Should be empty if mode
is `ISTIO_MUTUAL`.
items:
type: string
type: array
required:
- mode
type: object
required:
- port
type: object
type: array
tls:
description: TLS related settings for connections to the
upstream service.
properties:
caCertificates:
description: 'OPTIONAL: The path to the file containing
certificate authority certificates to use in verifying
a presented server certificate. If omitted, the proxy
will not verify the server''s certificate. Should
be empty if mode is `ISTIO_MUTUAL`.'
type: string
clientCertificate:
description: REQUIRED if mode is `MUTUAL`. The path
to the file holding the client-side TLS certificate
to use. Should be empty if mode is `ISTIO_MUTUAL`.
type: string
mode:
description: 'REQUIRED: Indicates whether connections
to this port should be secured using TLS. The value
of this field determines how TLS is enforced.'
type: string
privateKey:
description: REQUIRED if mode is `MUTUAL`. The path
to the file holding the client's private key. Should
be empty if mode is `ISTIO_MUTUAL`.
type: string
sni:
description: SNI string to present to the server during
TLS handshake. Should be empty if mode is `ISTIO_MUTUAL`.
type: string
subjectAltNames:
description: A list of alternate names to verify the
subject identity in the certificate. If specified,
the proxy will verify that the server certificate's
subject alt name matches one of the specified values.
Should be empty if mode is `ISTIO_MUTUAL`.
items:
type: string
type: array
required:
- mode
type: object
type: object
required:
- host
type: object
type: object
type: object
status:
type: object
version: v1alpha2
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,9 @@
apiVersion: servicemesh.kubesphere.io/v1alpha2
kind: ServicePolicy
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: servicepolicy-sample
spec:
# Add fields here
foo: bar

View File

@@ -0,0 +1,127 @@
/*
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 v1alpha2
import (
"github.com/knative/pkg/apis/istio/v1alpha3"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// ServicePolicySpec defines the desired state of ServicePolicy
type ServicePolicySpec struct {
// Label selector for destination rules.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty"`
// Template used to create a destination rule
// +optional
Template DestinationRuleSpecTemplate `json:"template,omitempty"`
}
type DestinationRuleSpecTemplate struct {
// Metadata of the virtual services created from this template
// +optional
metav1.ObjectMeta
// Spec indicates the behavior of a destination rule.
// +optional
Spec v1alpha3.DestinationRuleSpec `json:"spec,omitempty"`
}
type ServicePolicyConditionType string
// These are valid conditions of a strategy.
const (
// StrategyComplete means the strategy has been delivered to istio.
ServicePolicyComplete ServicePolicyConditionType = "Complete"
// StrategyFailed means the strategy has failed its delivery to istio.
ServicePolicyFailed ServicePolicyConditionType = "Failed"
)
// StrategyCondition describes current state of a strategy.
type ServicePolicyCondition struct {
// Type of strategy condition, Complete or Failed.
Type ServicePolicyConditionType
// Status of the condition, one of True, False, Unknown
Status apiextensions.ConditionStatus
// Last time the condition was checked.
// +optional
LastProbeTime metav1.Time
// Last time the condition transit from one status to another
// +optional
LastTransitionTime metav1.Time
// reason for the condition's last transition
Reason string
// Human readable message indicating details about last transition.
// +optinal
Message string
}
// ServicePolicyStatus defines the observed state of ServicePolicy
type ServicePolicyStatus struct {
// The latest available observations of an object's current state.
// +optional
Conditions []ServicePolicyCondition
// Represents time when the strategy was acknowledged by the controller.
// It is represented in RFC3339 form and is in UTC.
// +optional
StartTime *metav1.Time
// Represents time when the strategy was completed.
// It is represented in RFC3339 form and is in UTC.
// +optional
CompletionTime *metav1.Time
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServicePolicy is the Schema for the servicepolicies API
// +k8s:openapi-gen=true
type ServicePolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ServicePolicySpec `json:"spec,omitempty"`
Status ServicePolicyStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServicePolicyList contains a list of ServicePolicy
type ServicePolicyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ServicePolicy `json:"items"`
}
func init() {
SchemeBuilder.Register(&ServicePolicy{}, &ServicePolicyList{})
}

View File

@@ -0,0 +1,58 @@
/*
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 v1alpha2
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 TestStorageServicePolicy(t *testing.T) {
key := types.NamespacedName{
Name: "foo",
Namespace: "default",
}
created := &ServicePolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
}}
g := gomega.NewGomegaWithT(t)
// Test Create
fetched := &ServicePolicy{}
g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
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)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred())
g.Expect(fetched).To(gomega.Equal(updated))
// Test Delete
g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred())
}

View File

@@ -25,6 +25,156 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DestinationRuleSpecTemplate) DeepCopyInto(out *DestinationRuleSpecTemplate) {
*out = *in
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRuleSpecTemplate.
func (in *DestinationRuleSpecTemplate) DeepCopy() *DestinationRuleSpecTemplate {
if in == nil {
return nil
}
out := new(DestinationRuleSpecTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServicePolicy) DeepCopyInto(out *ServicePolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicy.
func (in *ServicePolicy) DeepCopy() *ServicePolicy {
if in == nil {
return nil
}
out := new(ServicePolicy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ServicePolicy) 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 *ServicePolicyCondition) DeepCopyInto(out *ServicePolicyCondition) {
*out = *in
in.LastProbeTime.DeepCopyInto(&out.LastProbeTime)
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyCondition.
func (in *ServicePolicyCondition) DeepCopy() *ServicePolicyCondition {
if in == nil {
return nil
}
out := new(ServicePolicyCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServicePolicyList) DeepCopyInto(out *ServicePolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ServicePolicy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyList.
func (in *ServicePolicyList) DeepCopy() *ServicePolicyList {
if in == nil {
return nil
}
out := new(ServicePolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ServicePolicyList) 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 *ServicePolicySpec) DeepCopyInto(out *ServicePolicySpec) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
in.Template.DeepCopyInto(&out.Template)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicySpec.
func (in *ServicePolicySpec) DeepCopy() *ServicePolicySpec {
if in == nil {
return nil
}
out := new(ServicePolicySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServicePolicyStatus) DeepCopyInto(out *ServicePolicyStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]ServicePolicyCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.StartTime != nil {
in, out := &in.StartTime, &out.StartTime
*out = (*in).DeepCopy()
}
if in.CompletionTime != nil {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyStatus.
func (in *ServicePolicyStatus) DeepCopy() *ServicePolicyStatus {
if in == nil {
return nil
}
out := new(ServicePolicyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Strategy) DeepCopyInto(out *Strategy) {
*out = *in

View File

@@ -28,6 +28,10 @@ type FakeServicemeshV1alpha2 struct {
*testing.Fake
}
func (c *FakeServicemeshV1alpha2) ServicePolicies(namespace string) v1alpha2.ServicePolicyInterface {
return &FakeServicePolicies{c, namespace}
}
func (c *FakeServicemeshV1alpha2) Strategies(namespace string) v1alpha2.StrategyInterface {
return &FakeStrategies{c, namespace}
}

View File

@@ -0,0 +1,140 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// 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"
v1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
)
// FakeServicePolicies implements ServicePolicyInterface
type FakeServicePolicies struct {
Fake *FakeServicemeshV1alpha2
ns string
}
var servicepoliciesResource = schema.GroupVersionResource{Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "servicepolicies"}
var servicepoliciesKind = schema.GroupVersionKind{Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Kind: "ServicePolicy"}
// Get takes name of the servicePolicy, and returns the corresponding servicePolicy object, and an error if there is any.
func (c *FakeServicePolicies) Get(name string, options v1.GetOptions) (result *v1alpha2.ServicePolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(servicepoliciesResource, c.ns, name), &v1alpha2.ServicePolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha2.ServicePolicy), err
}
// List takes label and field selectors, and returns the list of ServicePolicies that match those selectors.
func (c *FakeServicePolicies) List(opts v1.ListOptions) (result *v1alpha2.ServicePolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(servicepoliciesResource, servicepoliciesKind, c.ns, opts), &v1alpha2.ServicePolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha2.ServicePolicyList{ListMeta: obj.(*v1alpha2.ServicePolicyList).ListMeta}
for _, item := range obj.(*v1alpha2.ServicePolicyList).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 servicePolicies.
func (c *FakeServicePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(servicepoliciesResource, c.ns, opts))
}
// Create takes the representation of a servicePolicy and creates it. Returns the server's representation of the servicePolicy, and an error, if there is any.
func (c *FakeServicePolicies) Create(servicePolicy *v1alpha2.ServicePolicy) (result *v1alpha2.ServicePolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(servicepoliciesResource, c.ns, servicePolicy), &v1alpha2.ServicePolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha2.ServicePolicy), err
}
// Update takes the representation of a servicePolicy and updates it. Returns the server's representation of the servicePolicy, and an error, if there is any.
func (c *FakeServicePolicies) Update(servicePolicy *v1alpha2.ServicePolicy) (result *v1alpha2.ServicePolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(servicepoliciesResource, c.ns, servicePolicy), &v1alpha2.ServicePolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha2.ServicePolicy), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeServicePolicies) UpdateStatus(servicePolicy *v1alpha2.ServicePolicy) (*v1alpha2.ServicePolicy, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(servicepoliciesResource, "status", c.ns, servicePolicy), &v1alpha2.ServicePolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha2.ServicePolicy), err
}
// Delete takes name of the servicePolicy and deletes it. Returns an error if one occurs.
func (c *FakeServicePolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(servicepoliciesResource, c.ns, name), &v1alpha2.ServicePolicy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeServicePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(servicepoliciesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha2.ServicePolicyList{})
return err
}
// Patch applies the patch and returns the patched servicePolicy.
func (c *FakeServicePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.ServicePolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(servicepoliciesResource, c.ns, name, pt, data, subresources...), &v1alpha2.ServicePolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha2.ServicePolicy), err
}

View File

@@ -18,4 +18,6 @@ limitations under the License.
package v1alpha2
type ServicePolicyExpansion interface{}
type StrategyExpansion interface{}

View File

@@ -27,6 +27,7 @@ import (
type ServicemeshV1alpha2Interface interface {
RESTClient() rest.Interface
ServicePoliciesGetter
StrategiesGetter
}
@@ -35,6 +36,10 @@ type ServicemeshV1alpha2Client struct {
restClient rest.Interface
}
func (c *ServicemeshV1alpha2Client) ServicePolicies(namespace string) ServicePolicyInterface {
return newServicePolicies(c, namespace)
}
func (c *ServicemeshV1alpha2Client) Strategies(namespace string) StrategyInterface {
return newStrategies(c, namespace)
}

View File

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

View File

@@ -53,6 +53,8 @@ func (f *genericInformer) Lister() cache.GenericLister {
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=servicemesh.kubesphere.io, Version=v1alpha2
case v1alpha2.SchemeGroupVersion.WithResource("servicepolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().ServicePolicies().Informer()}, nil
case v1alpha2.SchemeGroupVersion.WithResource("strategies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().Strategies().Informer()}, nil

View File

@@ -24,6 +24,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// ServicePolicies returns a ServicePolicyInformer.
ServicePolicies() ServicePolicyInformer
// Strategies returns a StrategyInformer.
Strategies() StrategyInformer
}
@@ -39,6 +41,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// ServicePolicies returns a ServicePolicyInformer.
func (v *version) ServicePolicies() ServicePolicyInformer {
return &servicePolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// Strategies returns a StrategyInformer.
func (v *version) Strategies() StrategyInformer {
return &strategyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View File

@@ -0,0 +1,89 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha2
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"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/servicemesh/v1alpha2"
)
// ServicePolicyInformer provides access to a shared informer and lister for
// ServicePolicies.
type ServicePolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha2.ServicePolicyLister
}
type servicePolicyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewServicePolicyInformer constructs a new informer for ServicePolicy 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 NewServicePolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredServicePolicyInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredServicePolicyInformer constructs a new informer for ServicePolicy 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 NewFilteredServicePolicyInformer(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.ServicemeshV1alpha2().ServicePolicies(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ServicemeshV1alpha2().ServicePolicies(namespace).Watch(options)
},
},
&servicemeshv1alpha2.ServicePolicy{},
resyncPeriod,
indexers,
)
}
func (f *servicePolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredServicePolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *servicePolicyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&servicemeshv1alpha2.ServicePolicy{}, f.defaultInformer)
}
func (f *servicePolicyInformer) Lister() v1alpha2.ServicePolicyLister {
return v1alpha2.NewServicePolicyLister(f.Informer().GetIndexer())
}

View File

@@ -18,6 +18,14 @@ limitations under the License.
package v1alpha2
// ServicePolicyListerExpansion allows custom methods to be added to
// ServicePolicyLister.
type ServicePolicyListerExpansion interface{}
// ServicePolicyNamespaceListerExpansion allows custom methods to be added to
// ServicePolicyNamespaceLister.
type ServicePolicyNamespaceListerExpansion interface{}
// StrategyListerExpansion allows custom methods to be added to
// StrategyLister.
type StrategyListerExpansion interface{}

View File

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

View File

@@ -17,13 +17,12 @@ limitations under the License.
package controller
import (
"kubesphere.io/kubesphere/pkg/controller/strategy"
"sigs.k8s.io/application/pkg/controller/application"
)
func init() {
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager.
AddToManagerFuncs = append(AddToManagerFuncs, strategy.Add)
//AddToManagerFuncs = append(AddToManagerFuncs, strategy.Add)
// Add application to manager functions
AddToManagerFuncs = append(AddToManagerFuncs, application.Add)

View File

@@ -16,6 +16,7 @@ import (
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/util/metrics"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
"kubesphere.io/kubesphere/pkg/controller/virtualservice/util"
"reflect"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
@@ -32,6 +33,9 @@ import (
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"time"
servicemeshinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh/v1alpha2"
servicemeshlisters "kubesphere.io/kubesphere/pkg/client/listers/servicemesh/v1alpha2"
)
const (
@@ -59,6 +63,9 @@ type DestinationRuleController struct {
deploymentLister listersv1.DeploymentLister
deploymentSynced cache.InformerSynced
servicePolicyLister servicemeshlisters.ServicePolicyLister
servicePolicySynced cache.InformerSynced
destinationRuleLister istiolisters.DestinationRuleLister
destinationRuleSynced cache.InformerSynced
@@ -70,6 +77,7 @@ type DestinationRuleController struct {
func NewDestinationRuleController(deploymentInformer informersv1.DeploymentInformer,
destinationRuleInformer istioinformers.DestinationRuleInformer,
serviceInformer coreinformers.ServiceInformer,
servicePolicyInformer servicemeshinformers.ServicePolicyInformer,
client clientset.Interface,
destinationRuleClient istioclientset.Interface) *DestinationRuleController {
@@ -116,6 +124,17 @@ func NewDestinationRuleController(deploymentInformer informersv1.DeploymentInfor
v.destinationRuleLister = destinationRuleInformer.Lister()
v.destinationRuleSynced = destinationRuleInformer.Informer().HasSynced
v.servicePolicyLister = servicePolicyInformer.Lister()
v.servicePolicySynced = servicePolicyInformer.Informer().HasSynced
servicePolicyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: v.addServicePolicy,
UpdateFunc: func(old, cur interface{}) {
v.addServicePolicy(cur)
},
DeleteFunc: v.addServicePolicy,
})
v.eventBroadcaster = broadcaster
v.eventRecorder = recorder
@@ -136,7 +155,7 @@ func (v *DestinationRuleController) Run(workers int, stopCh <-chan struct{}) {
log.Info("starting destinationrule controller")
defer log.Info("shutting down destinationrule controller")
if !controller.WaitForCacheSync("destinationrule-controller", stopCh, v.serviceSynced, v.destinationRuleSynced, v.deploymentSynced) {
if !controller.WaitForCacheSync("destinationrule-controller", stopCh, v.serviceSynced, v.destinationRuleSynced, v.deploymentSynced, v.servicePolicySynced) {
return
}
@@ -177,6 +196,8 @@ func (v *DestinationRuleController) processNextWorkItem() bool {
return true
}
// main function of the reconcile for destinationrule
// destinationrule's name is same with the service that created it
func (v *DestinationRuleController) syncService(key string) error {
startTime := time.Now()
defer func() {
@@ -192,14 +213,14 @@ func (v *DestinationRuleController) syncService(key string) error {
if err != nil {
// Delete the corresponding destinationrule, as the service has been deleted.
err = v.destinationRuleClient.NetworkingV1alpha3().DestinationRules(namespace).Delete(name, nil)
if err != nil && !errors.IsNotFound(err) {
if !errors.IsNotFound(err) {
log.Error(err, "delete destination rule failed", "namespace", namespace, "name", name)
return err
}
return nil
}
if len(service.Labels) < len(util.ApplicationLabels) || !util.IsApplicationComponent(&service.ObjectMeta) ||
if len(service.Labels) < len(util.ApplicationLabels) || !util.IsApplicationComponent(service.Labels) ||
len(service.Spec.Ports) == 0 {
// services don't have enough labels to create a virtualservice
// or they don't have necessary labels
@@ -207,14 +228,22 @@ func (v *DestinationRuleController) syncService(key string) error {
return nil
}
appName := util.GetComponentName(&service.ObjectMeta)
// fetch all deployments that match with service selector
deployments, err := v.deploymentLister.Deployments(namespace).List(labels.Set(service.Spec.Selector).AsSelectorPreValidated())
if err != nil {
return err
}
subsets := []v1alpha3.Subset{}
subsets := make([]v1alpha3.Subset, 0)
for _, deployment := range deployments {
// not a valid deployment we required
if !util.IsApplicationComponent(deployment.Labels) || !util.IsApplicationComponent(deployment.Spec.Selector.MatchLabels) {
continue
}
version := util.GetComponentVersion(&deployment.ObjectMeta)
if len(version) == 0 {
@@ -248,19 +277,49 @@ func (v *DestinationRuleController) syncService(key string) error {
log.Error(err, "Couldn't get destinationrule for service", "key", key)
return err
}
}
// fetch all servicepolicies associated to this service
servicePolicies, err := v.servicePolicyLister.ServicePolicies(namespace).List(labels.SelectorFromSet(map[string]string{util.AppLabel: appName}))
if err != nil {
log.Error(err, "could not list service policies is namespace with component name", "namespace", namespace, "name", appName)
return err
}
dr := currentDestinationRule.DeepCopy()
dr.Spec.Subsets = subsets
//
if len(servicePolicies) > 0 {
if len(servicePolicies) > 1 {
err = fmt.Errorf("more than one service policy associated with service %s/%s is forbidden", namespace, name)
log.Error(err, "")
return err
}
sp := servicePolicies[0]
if sp.Spec.Template.Spec.TrafficPolicy != nil {
dr.Spec.TrafficPolicy = sp.Spec.Template.Spec.TrafficPolicy
}
for _, subset := range sp.Spec.Template.Spec.Subsets {
for i := range dr.Spec.Subsets {
if subset.Name == dr.Spec.Subsets[i].Name && subset.TrafficPolicy != nil {
dr.Spec.Subsets[i].TrafficPolicy = subset.TrafficPolicy
}
}
}
}
createDestinationRule := len(currentDestinationRule.ResourceVersion) == 0
if !createDestinationRule && reflect.DeepEqual(currentDestinationRule.Spec.Subsets, subsets) &&
if !createDestinationRule && reflect.DeepEqual(currentDestinationRule.Spec, dr.Spec) &&
reflect.DeepEqual(currentDestinationRule.Labels, service.Labels) {
log.V(5).Info("destinationrule are equal, skipping update", "key", types.NamespacedName{Namespace: service.Namespace, Name: service.Name}.String())
return nil
}
newDestinationRule := currentDestinationRule.DeepCopy()
newDestinationRule.Spec.Subsets = subsets
newDestinationRule.Spec = dr.Spec
newDestinationRule.Labels = service.Labels
if newDestinationRule.Annotations == nil {
newDestinationRule.Annotations = make(map[string]string)
@@ -293,20 +352,13 @@ func (v *DestinationRuleController) syncService(key string) error {
return nil
}
func (v *DestinationRuleController) isApplicationComponent(meta *metav1.ObjectMeta) bool {
if len(meta.Labels) >= len(util.ApplicationLabels) && util.IsApplicationComponent(meta) {
return true
}
return false
}
// When a destinationrule is added, figure out which service it will be used
// and enqueue it. obj must have *appsv1.Deployment type
func (v *DestinationRuleController) addDeployment(obj interface{}) {
deploy := obj.(*appsv1.Deployment)
// not a application component
if !v.isApplicationComponent(&deploy.ObjectMeta) {
if !util.IsApplicationComponent(deploy.Labels) || !util.IsApplicationComponent(deploy.Spec.Selector.MatchLabels) {
return
}
@@ -354,7 +406,7 @@ func (v *DestinationRuleController) getDeploymentServiceMemberShip(deployment *a
for i := range allServices {
service := allServices[i]
if service.Spec.Selector == nil || !v.isApplicationComponent(&service.ObjectMeta) {
if service.Spec.Selector == nil || !util.IsApplicationComponent(service.Labels) {
// services with nil selectors match nothing, not everything.
continue
}
@@ -371,6 +423,34 @@ func (v *DestinationRuleController) getDeploymentServiceMemberShip(deployment *a
return set, nil
}
func (v *DestinationRuleController) addServicePolicy(obj interface{}) {
servicePolicy := obj.(*servicemeshv1alpha2.ServicePolicy)
appName := servicePolicy.Labels[util.AppLabel]
services, err := v.serviceLister.Services(servicePolicy.Namespace).List(labels.SelectorFromSet(map[string]string{util.AppLabel: appName}))
if err != nil {
log.Error(err, "cannot list services", "namespace", servicePolicy.Namespace, "name", appName)
utilruntime.HandleError(fmt.Errorf("cannot list services in namespace %s, with component name %v", servicePolicy.Namespace, appName))
return
}
set := sets.String{}
for _, service := range services {
key, err := controller.KeyFunc(service)
if err != nil {
utilruntime.HandleError(err)
continue
}
set.Insert(key)
}
// avoid enqueue a key multiple times
for key := range set {
v.queue.Add(key)
}
}
func (v *DestinationRuleController) handleErr(err error, key interface{}) {
if err != nil {
v.queue.Forget(key)
@@ -383,7 +463,7 @@ func (v *DestinationRuleController) handleErr(err error, key interface{}) {
return
}
log.V(0).Info("Dropping service out of the queue", "key", key, "error", err)
log.V(4).Info("Dropping service out of the queue", "key", key, "error", err)
v.queue.Forget(key)
utilruntime.HandleError(err)
}

View File

@@ -1 +1,58 @@
package destinationrule
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// TODO(jeff): add test cases
var namespace = "default"
var lbs = map[string]string{
"app.kubernetes.io/name": "bookinfo",
"servicemesh.kubesphere.io/enabled": "",
"app": "reviews",
}
var service = corev1.Service{}
var deployments = []appsv1.Deployment{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: "deploy-v1",
Labels: map[string]string{
"app.kubernetes.io/name": "bookinfo",
"servicemesh.kubesphere.io/enabled": "",
"app": "reviews",
"version": "v1",
},
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app.kubernetes.io/name": "bookinfo",
"servicemesh.kubesphere.io/enabled": "",
"app": "reviews",
"version": "v1",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app.kubernetes.io/name": "bookinfo",
"servicemesh.kubesphere.io/enabled": "",
"app": "reviews",
"version": "v1",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{},
},
},
},
},
},
}

View File

@@ -1,51 +0,0 @@
package strategy
import (
"fmt"
"github.com/knative/pkg/apis/istio/v1alpha3"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
)
const (
AppLabel = "app"
)
func getAppNameByStrategy(strategy *v1alpha2.Strategy) string {
if len(strategy.Labels) > 0 && len(strategy.Labels[AppLabel]) > 0 {
return strategy.Labels[AppLabel]
}
return ""
}
// if virtualservice not specified with port number, then fill with service first port
func fillDestinationPort(vs *v1alpha3.VirtualService, service *v1.Service) error {
if len(service.Spec.Ports) == 0 {
return fmt.Errorf("service %s/%s spec doesn't canotain any ports", service.Namespace, service.Name)
}
// fill http port
for i := range vs.Spec.Http {
for j := range vs.Spec.Http[i].Route {
if vs.Spec.Http[i].Route[j].Destination.Port.Number == 0 {
vs.Spec.Http[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
}
}
if vs.Spec.Http[i].Mirror != nil && vs.Spec.Http[i].Mirror.Port.Number == 0 {
vs.Spec.Http[i].Mirror.Port.Number = uint32(service.Spec.Ports[0].Port)
}
}
// fill tcp port
for i := range vs.Spec.Tcp {
for j := range vs.Spec.Tcp[i].Route {
if vs.Spec.Tcp[i].Route[j].Destination.Port.Number == 0 {
vs.Spec.Tcp[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
}
}
}
return nil
}

View File

@@ -1,192 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package strategy
import (
"context"
"fmt"
"github.com/knative/pkg/apis/istio/v1alpha3"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
"reflect"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("strategy-controller")
// Add creates a new Strategy Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileStrategy{Client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("strategy-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return err
}
// Watch for changes to Strategy
err = c.Watch(&source.Kind{Type: &servicemeshv1alpha2.Strategy{}}, &handler.EnqueueRequestForObject{})
if err != nil {
return err
}
// TODO(user): Modify this to be the types you create
// Watch a VirtualService created by Strategy
err = c.Watch(&source.Kind{Type: &v1alpha3.VirtualService{}}, &handler.EnqueueRequestForOwner{
IsController: true,
OwnerType: &servicemeshv1alpha2.Strategy{},
})
if err != nil {
return err
}
return nil
}
var _ reconcile.Reconciler = &ReconcileStrategy{}
// ReconcileStrategy reconciles a Strategy object
type ReconcileStrategy struct {
client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a Strategy object and makes changes based on the state read
// and what is in the Strategy.Spec
// a Deployment as an example
// Automatically generate RBAC rules to allow the Controller to read and write Deployments
// +kubebuilder:rbac:groups=networking.istio.io,resources=virtualservices,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=networking.istio.io,resources=virtualservices/status,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=servicemesh.kubesphere.io,resources=strategies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=servicemesh.kubesphere.io,resources=strategies/status,verbs=get;update;patch
func (r *ReconcileStrategy) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Fetch the Strategy instance
strategy := &servicemeshv1alpha2.Strategy{}
err := r.Get(context.TODO(), request.NamespacedName, strategy)
if err != nil {
if errors.IsNotFound(err) {
return reconcile.Result{}, nil
}
return reconcile.Result{}, err
}
return r.reconcileStrategy(strategy)
}
func (r *ReconcileStrategy) reconcileStrategy(strategy *servicemeshv1alpha2.Strategy) (reconcile.Result, error) {
appName := getAppNameByStrategy(strategy)
service := &v1.Service{}
err := r.Get(context.TODO(), types.NamespacedName{Namespace: strategy.Namespace, Name: appName}, service)
if err != nil {
log.Error(err, "couldn't find service", "namespace", strategy.Namespace, "name", appName)
return reconcile.Result{}, errors.NewBadRequest(fmt.Sprintf("service %s not found", appName))
}
vs, err := r.generateVirtualService(strategy, service)
// Check if the VirtualService already exists
found := &v1alpha3.VirtualService{}
err = r.Get(context.TODO(), types.NamespacedName{Name: vs.Name, Namespace: vs.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
log.Info("Creating VirtualService", "namespace", vs.Namespace, "name", vs.Name)
err = r.Create(context.TODO(), vs)
return reconcile.Result{}, err
} else if err != nil {
return reconcile.Result{}, err
}
// Update the found object and write the result back if there are any changes
if !reflect.DeepEqual(vs.Spec, found.Spec) || len(vs.OwnerReferences) == 0 {
found.Spec = vs.Spec
found.OwnerReferences = vs.OwnerReferences
log.Info("Updating VirtualService", "namespace", vs.Namespace, "name", vs.Name)
err = r.Update(context.TODO(), found)
if err != nil {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, nil
}
func (r *ReconcileStrategy) generateVirtualService(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) (*v1alpha3.VirtualService, error) {
// Define VirtualService to be created
vs := &v1alpha3.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: getAppNameByStrategy(strategy),
Namespace: strategy.Namespace,
Labels: strategy.Spec.Selector.MatchLabels,
},
Spec: strategy.Spec.Template.Spec,
}
// one version rules them all
if len(strategy.Spec.GovernorVersion) > 0 {
governorDestinationWeight := v1alpha3.DestinationWeight{
Destination: v1alpha3.Destination{
Host: getAppNameByStrategy(strategy),
Subset: strategy.Spec.GovernorVersion,
},
Weight: 100,
}
if len(strategy.Spec.Template.Spec.Http) > 0 {
governorRoute := v1alpha3.HTTPRoute{
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
}
vs.Spec.Http = []v1alpha3.HTTPRoute{governorRoute}
} else if len(strategy.Spec.Template.Spec.Tcp) > 0 {
governorRoute := v1alpha3.TCPRoute{
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
}
vs.Spec.Tcp = []v1alpha3.TCPRoute{governorRoute}
}
}
if err := fillDestinationPort(vs, service); err != nil {
return nil, err
}
return vs, nil
}

View File

@@ -1,75 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package strategy
import (
stdlog "log"
"os"
"path/filepath"
"sync"
"testing"
"github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"kubesphere.io/kubesphere/pkg/apis"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var cfg *rest.Config
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crds")},
}
apis.AddToScheme(scheme.Scheme)
var err error
if cfg, err = t.Start(); err != nil {
stdlog.Fatal(err)
}
code := m.Run()
t.Stop()
os.Exit(code)
}
// SetupTestReconcile returns a reconcile.Reconcile implementation that delegates to inner and
// writes the request to requests after Reconcile is finished.
func SetupTestReconcile(inner reconcile.Reconciler) (reconcile.Reconciler, chan reconcile.Request) {
requests := make(chan reconcile.Request)
fn := reconcile.Func(func(req reconcile.Request) (reconcile.Result, error) {
result, err := inner.Reconcile(req)
requests <- req
return result, err
})
return fn, requests
}
// StartTestManager adds recFn
func StartTestManager(mgr manager.Manager, g *gomega.GomegaWithT) (chan struct{}, *sync.WaitGroup) {
stop := make(chan struct{})
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
g.Expect(mgr.Start(stop)).NotTo(gomega.HaveOccurred())
}()
return stop, wg
}

View File

@@ -1,177 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package strategy
import (
"github.com/knative/pkg/apis/istio/common/v1alpha1"
"github.com/knative/pkg/apis/istio/v1alpha3"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/json"
"testing"
"time"
"github.com/onsi/gomega"
"golang.org/x/net/context"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
var c client.Client
var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
var depKey = types.NamespacedName{Name: "details", Namespace: "default"}
const timeout = time.Second * 5
var labels = map[string]string{
"app.kubernetes.io/name": "details",
"app.kubernetes.io/version": "v1",
"app": "details",
"servicemesh.kubesphere.io/enabled": "",
}
var svc = v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "details",
Namespace: "default",
Labels: labels,
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
Name: "http",
Port: 8080,
Protocol: v1.ProtocolTCP,
},
},
Selector: labels,
},
}
func TestReconcile(t *testing.T) {
g := gomega.NewGomegaWithT(t)
instance := &servicemeshv1alpha2.Strategy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
Labels: labels,
},
Spec: servicemeshv1alpha2.StrategySpec{
Type: servicemeshv1alpha2.CanaryType,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: servicemeshv1alpha2.VirtualServiceTemplateSpec{
Spec: v1alpha3.VirtualServiceSpec{
Hosts: []string{"details"},
Gateways: []string{"default"},
Http: []v1alpha3.HTTPRoute{
{
Match: []v1alpha3.HTTPMatchRequest{
{
Method: &v1alpha1.StringMatch{
Exact: "POST",
},
},
},
Route: []v1alpha3.DestinationWeight{
{
Destination: v1alpha3.Destination{
Host: "details",
Subset: "v1",
},
Weight: 60,
},
},
},
{
Route: []v1alpha3.DestinationWeight{
{
Destination: v1alpha3.Destination{
Host: "details",
Subset: "v2",
},
Weight: 40,
},
},
},
},
},
},
},
}
// Setup the Manager and Controller. Wrap the Controller Reconcile function so it writes each request to a
// channel when it is finished.
mgr, err := manager.New(cfg, manager.Options{})
g.Expect(err).NotTo(gomega.HaveOccurred())
c = mgr.GetClient()
recFn, requests := SetupTestReconcile(newReconciler(mgr))
g.Expect(add(mgr, recFn)).NotTo(gomega.HaveOccurred())
stopMgr, mgrStopped := StartTestManager(mgr, g)
defer func() {
close(stopMgr)
mgrStopped.Wait()
}()
err = c.Create(context.TODO(), &svc)
if apierrors.IsInvalid(err) {
t.Logf("failed to create service, %v", err)
return
}
g.Expect(err).NotTo(gomega.HaveOccurred())
//defer c.Delete(context.TODO(), &svc)
// Create the Strategy object and expect the Reconcile and Deployment to be created
err = c.Create(context.TODO(), instance)
// The instance object may not be a valid object because it might be missing some required fields.
// Please modify the instance object by adding required fields and then remove the following if statement.
if apierrors.IsInvalid(err) {
t.Logf("failed to create object, got an invalid object error: %v", err)
return
}
g.Expect(err).NotTo(gomega.HaveOccurred())
defer c.Delete(context.TODO(), instance)
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
vs := &v1alpha3.VirtualService{}
g.Eventually(func() error { return c.Get(context.TODO(), depKey, vs) }, timeout).
Should(gomega.Succeed())
if str, err := json.Marshal(vs); err == nil {
t.Logf("Created virtual service %s\n", str)
}
// Delete the Deployment and expect Reconcile to be called for Deployment deletion
g.Expect(c.Delete(context.TODO(), vs)).NotTo(gomega.HaveOccurred())
//g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
//g.Eventually(func() error { return c.Get(context.TODO(), depKey, vs) }, timeout).Should(gomega.Succeed())
// Manually delete Deployment since GC isn't enabled in the test control plane
g.Eventually(func() error { return c.Delete(context.TODO(), vs) }, timeout).
Should(gomega.MatchError("virtualservices.networking.istio.io \"details\" not found"))
}

View File

@@ -1,6 +1,8 @@
package util
import (
"github.com/knative/pkg/apis/istio/v1alpha3"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
)
@@ -47,9 +49,9 @@ func GetComponentVersion(meta *metav1.ObjectMeta) string {
func ExtractApplicationLabels(meta *metav1.ObjectMeta) map[string]string {
labels := make(map[string]string, 0)
labels := make(map[string]string, len(ApplicationLabels))
for _, label := range ApplicationLabels {
if len(meta.Labels[label]) == 0 {
if _, ok := meta.Labels[label]; !ok {
return nil
} else {
labels[label] = meta.Labels[label]
@@ -59,13 +61,38 @@ func ExtractApplicationLabels(meta *metav1.ObjectMeta) map[string]string {
return labels
}
func IsApplicationComponent(meta *metav1.ObjectMeta) bool {
func IsApplicationComponent(lbs map[string]string) bool {
for _, label := range ApplicationLabels {
if len(meta.Labels[label]) == 0 {
if _, ok := lbs[label]; !ok {
return false
}
}
return true
}
// if virtualservice not specified with port number, then fill with service first port
func FillDestinationPort(vs *v1alpha3.VirtualService, service *v1.Service) {
// fill http port
for i := range vs.Spec.Http {
for j := range vs.Spec.Http[i].Route {
if vs.Spec.Http[i].Route[j].Destination.Port.Number == 0 {
vs.Spec.Http[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
}
}
if vs.Spec.Http[i].Mirror != nil && vs.Spec.Http[i].Mirror.Port.Number == 0 {
vs.Spec.Http[i].Mirror.Port.Number = uint32(service.Spec.Ports[0].Port)
}
}
// fill tcp port
for i := range vs.Spec.Tcp {
for j := range vs.Spec.Tcp[i].Route {
if vs.Spec.Tcp[i].Route[j].Destination.Port.Number == 0 {
vs.Spec.Tcp[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
}
}
}
}

View File

@@ -6,14 +6,17 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes/scheme"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/util/metrics"
"kubesphere.io/kubesphere/pkg/controller/virtualservice/util"
"reflect"
"strings"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
@@ -27,6 +30,7 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
servicemeshinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh/v1alpha2"
servicemeshlisters "kubesphere.io/kubesphere/pkg/client/listers/servicemesh/v1alpha2"
@@ -98,6 +102,7 @@ func NewVirtualServiceController(serviceInformer coreinformers.ServiceInformer,
AddFunc: v.enqueueService,
DeleteFunc: v.enqueueService,
UpdateFunc: func(old, cur interface{}) {
// TODO(jeff): need a more robust mechanism, because user may change labels
v.enqueueService(cur)
},
})
@@ -109,7 +114,11 @@ func NewVirtualServiceController(serviceInformer coreinformers.ServiceInformer,
v.strategySynced = strategyInformer.Informer().HasSynced
strategyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
DeleteFunc: v.deleteStrategy,
DeleteFunc: v.addStrategy,
AddFunc: v.addStrategy,
UpdateFunc: func(old, cur interface{}) {
v.addStrategy(cur)
},
})
v.destinationRuleLister = destinationRuleInformer.Lister()
@@ -185,48 +194,63 @@ func (v *VirtualServiceController) processNextWorkItem() bool {
return true
}
// created virtualservice's name are same as the service name, same
// as the destinationrule name
// labels:
// servicemesh.kubernetes.io/enabled: ""
// app.kubernetes.io/name: bookinfo
// app: reviews
// are used to bind them together.
// syncService are the main part of reconcile function body, it takes
// service, destinationrule, strategy as input to create a virtualservice
// for service.
func (v *VirtualServiceController) syncService(key string) error {
startTime := time.Now()
defer func() {
log.V(4).Info("Finished syncing service virtualservice. ", "service", key, "duration", time.Since(startTime))
}()
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
log.Error(err, "not a valid controller key", "key", key)
return err
}
// default component name to service name
appName := name
defer func() {
log.V(4).Info("Finished syncing service virtualservice.", "namespace", namespace, "name", name, "duration", time.Since(startTime))
}()
service, err := v.serviceLister.Services(namespace).Get(name)
if err != nil {
// Delete the corresponding virtualservice, as the service has been deleted.
err = v.virtualServiceClient.NetworkingV1alpha3().VirtualServices(namespace).Delete(name, nil)
if err != nil && !errors.IsNotFound(err) {
log.Error(err, "delete orphan virtualservice failed", "namespace", service.Namespace, "name", service.Name)
return err
if errors.IsNotFound(err) {
// Delete the corresponding virtualservice, as the service has been deleted.
err = v.virtualServiceClient.NetworkingV1alpha3().VirtualServices(namespace).Delete(name, nil)
if err != nil && !errors.IsNotFound(err) {
log.Error(err, "delete orphan virtualservice failed", "namespace", namespace, "name", service.Name)
return err
}
return nil
}
return nil
log.Error(err, "get service failed", "namespace", namespace, "name", name)
return err
}
if len(service.Labels) < len(util.ApplicationLabels) || !util.IsApplicationComponent(&service.ObjectMeta) ||
if len(service.Labels) < len(util.ApplicationLabels) || !util.IsApplicationComponent(service.Labels) ||
len(service.Spec.Ports) == 0 {
// services don't have enough labels to create a virtualservice
// or they don't have necessary labels
// or they don't have any ports defined
return nil
}
vs, err := v.virtualServiceLister.VirtualServices(namespace).Get(name)
if err == nil {
// there already is virtual service there, no need to create another one
return nil
}
// get real component name, i.e label app value
appName = util.GetComponentName(&service.ObjectMeta)
destinationRule, err := v.destinationRuleLister.DestinationRules(namespace).Get(name)
if err != nil {
if errors.IsNotFound(err) {
// there is no destinationrule for this service
// maybe corresponding workloads are not created yet
return nil
log.Info("destination rules for service not found, retrying.", "namespace", namespace, "name", name)
return fmt.Errorf("destination rule for service %s/%s not found", namespace, name)
}
log.Error(err, "Couldn't get destinationrule for service.", "service", types.NamespacedName{Name: service.Name, Namespace: service.Namespace}.String())
return err
@@ -235,20 +259,47 @@ func (v *VirtualServiceController) syncService(key string) error {
subsets := destinationRule.Spec.Subsets
if len(subsets) == 0 {
// destination rule with no subsets, not possibly
err = fmt.Errorf("find destinationrule with no subsets for service %s", name)
log.Error(err, "Find destinationrule with no subsets for service", "namespace", service.Namespace, "name", name)
err = fmt.Errorf("found destinationrule with no subsets for service %s", name)
log.Error(err, "found destinationrule with no subsets", "namespace", namespace, "name", appName)
return err
} else {
vs = &v1alpha3.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: util.ExtractApplicationLabels(&service.ObjectMeta),
},
Spec: v1alpha3.VirtualServiceSpec{
Hosts: []string{name},
},
}
// fetch all strategies applied to service
strategies, err := v.strategyLister.Strategies(namespace).List(labels.SelectorFromSet(map[string]string{util.AppLabel: appName}))
if err != nil {
log.Error(err, "list strategies for service failed", "namespace", namespace, "name", appName)
return err
} else if len(strategies) > 1 {
// more than one strategies are not allowed, it will cause collision
err = fmt.Errorf("more than one strategies applied to service %s/%s is forbbiden", namespace, appName)
log.Error(err, "")
return err
}
// get current virtual service
currentVirtualService, err := v.virtualServiceLister.VirtualServices(namespace).Get(appName)
if err != nil {
if errors.IsNotFound(err) {
currentVirtualService = &v1alpha3.VirtualService{
ObjectMeta: metav1.ObjectMeta{
Name: appName,
Namespace: namespace,
Labels: util.ExtractApplicationLabels(&service.ObjectMeta),
},
}
}
return nil
}
vs := currentVirtualService.DeepCopy()
if len(strategies) > 0 {
// apply strategy spec to virtualservice
vs.Spec = v.generateVirtualServiceSpec(strategies[0], service).Spec
} else {
// create a whole new virtualservice
// TODO(jeff): use FQDN to replace service name
vs.Spec.Hosts = []string{name}
// check if service has TCP protocol ports
for _, port := range service.Spec.Ports {
@@ -275,18 +326,45 @@ func (v *VirtualServiceController) syncService(key string) error {
vs.Spec.Tcp = []v1alpha3.TCPRoute{{Route: []v1alpha3.DestinationWeight{route}}}
}
}
}
if len(vs.Spec.Http) > 0 || len(vs.Spec.Tcp) > 0 {
_, err := v.virtualServiceClient.NetworkingV1alpha3().VirtualServices(namespace).Create(vs)
if err != nil {
v.eventRecorder.Event(vs, v1.EventTypeWarning, "FailedToCreateVirtualService", fmt.Sprintf("Failed to create virtualservice for service %v/%v: %v", service.Namespace, service.Name, err))
log.Error(err, "create virtualservice for service failed.", "service", service)
return err
}
createVirtualService := len(currentVirtualService.ResourceVersion) == 0
if !createVirtualService &&
reflect.DeepEqual(vs.Spec, currentVirtualService.Spec) &&
reflect.DeepEqual(service.Labels, currentVirtualService.Labels) {
log.V(4).Info("virtual service are equal, skipping update ")
return nil
}
newVirtualService := currentVirtualService.DeepCopy()
newVirtualService.Labels = service.Labels
newVirtualService.Spec = vs.Spec
if newVirtualService.Annotations == nil {
newVirtualService.Annotations = make(map[string]string)
}
if len(newVirtualService.Spec.Http) == 0 && len(newVirtualService.Spec.Tcp) == 0 && len(newVirtualService.Spec.Tls) == 0 {
err = fmt.Errorf("service %s/%s doesn't have a valid port spec", namespace, name)
log.Error(err, "")
return err
}
if createVirtualService {
_, err = v.virtualServiceClient.NetworkingV1alpha3().VirtualServices(namespace).Create(newVirtualService)
} else {
_, err = v.virtualServiceClient.NetworkingV1alpha3().VirtualServices(namespace).Update(newVirtualService)
}
if err != nil {
if createVirtualService {
v.eventRecorder.Event(newVirtualService, v1.EventTypeWarning, "FailedToCreateVirtualService", fmt.Sprintf("Failed to create virtualservice for service %v/%v: %v", namespace, name, err))
} else {
log.Info("service doesn't have a tcp port.")
return nil
v.eventRecorder.Event(newVirtualService, v1.EventTypeWarning, "FailedToUpdateVirtualService", fmt.Sprintf("Failed to update virtualservice for service %v/%v: %v", namespace, name, err))
}
return err
}
return nil
@@ -299,7 +377,7 @@ func (v *VirtualServiceController) addDestinationRule(obj interface{}) {
service, err := v.serviceLister.Services(dr.Namespace).Get(dr.Name)
if err != nil {
if errors.IsNotFound(err) {
log.V(0).Info("service not created yet", "namespace", dr.Namespace, "service", dr.Name)
log.V(3).Info("service not created yet", "namespace", dr.Namespace, "service", dr.Name)
return
}
utilruntime.HandleError(fmt.Errorf("unable to get service with name %s/%s", dr.Namespace, dr.Name))
@@ -324,8 +402,46 @@ func (v *VirtualServiceController) addDestinationRule(obj interface{}) {
return
}
func (v *VirtualServiceController) deleteStrategy(obj interface{}) {
// nothing to do right now
// when a strategy created
func (v *VirtualServiceController) addStrategy(obj interface{}) {
strategy := obj.(*servicemeshv1alpha2.Strategy)
lbs := util.ExtractApplicationLabels(&strategy.ObjectMeta)
if len(lbs) == 0 {
err := fmt.Errorf("invalid strategy %s/%s labels %s, not have required labels", strategy.Namespace, strategy.Name, strategy.Labels)
log.Error(err, "")
utilruntime.HandleError(err)
return
}
allServices, err := v.serviceLister.Services(strategy.Namespace).List(labels.SelectorFromSet(lbs))
if err != nil {
log.Error(err, "list services failed")
utilruntime.HandleError(err)
return
}
// avoid insert a key multiple times
set := sets.String{}
for i := range allServices {
service := allServices[i]
if service.Spec.Selector == nil || len(service.Spec.Ports) == 0 {
// services with nil selectors match nothing, not everything.
continue
}
key, err := controller.KeyFunc(service)
if err != nil {
utilruntime.HandleError(err)
return
}
set.Insert(key)
}
for key := range set {
v.queue.Add(key)
}
}
func (v *VirtualServiceController) handleErr(err error, key interface{}) {
@@ -344,3 +460,40 @@ func (v *VirtualServiceController) handleErr(err error, key interface{}) {
v.queue.Forget(key)
utilruntime.HandleError(err)
}
func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) *v1alpha3.VirtualService {
// Define VirtualService to be created
vs := &v1alpha3.VirtualService{
Spec: strategy.Spec.Template.Spec,
}
// one version rules them all
if len(strategy.Spec.GovernorVersion) > 0 {
governorDestinationWeight := v1alpha3.DestinationWeight{
Destination: v1alpha3.Destination{
Host: service.Name,
Subset: strategy.Spec.GovernorVersion,
},
Weight: 100,
}
if len(strategy.Spec.Template.Spec.Http) > 0 {
governorRoute := v1alpha3.HTTPRoute{
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
}
vs.Spec.Http = []v1alpha3.HTTPRoute{governorRoute}
} else if len(strategy.Spec.Template.Spec.Tcp) > 0 {
governorRoute := v1alpha3.TCPRoute{
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
}
vs.Spec.Tcp = []v1alpha3.TCPRoute{governorRoute}
}
}
util.FillDestinationPort(vs, service)
return vs
}

View File

@@ -34,12 +34,12 @@ var (
k8sClient *kubernetes.Clientset
k8sClientOnce sync.Once
KubeConfig *rest.Config
masterURL string
masterURL string
)
func init() {
flag.StringVar(&kubeConfigFile, "kubeconfig", "", "path to kubeconfig file")
flag.StringVar(&masterURL, "master-url","", "kube-apiserver url, only needed when out of cluster")
flag.StringVar(&masterURL, "master-url", "", "kube-apiserver url, only needed when out of cluster")
}
func Client() *kubernetes.Clientset {