change cluster schema (#2026)

* change cluster schema

* change cluster schema
This commit is contained in:
zryfish
2020-04-27 17:34:02 +08:00
committed by GitHub
parent 794f388306
commit 5a3eb651f3
123 changed files with 13582 additions and 1032 deletions

View File

@@ -17202,10 +17202,10 @@
},
"v1alpha2.Node": {
"required": [
"rank",
"labelMinor",
"id",
"label",
"labelMinor",
"rank",
"controls"
],
"properties": {
@@ -17313,10 +17313,10 @@
},
"v1alpha2.NodeSummary": {
"required": [
"labelMinor",
"rank",
"id",
"label"
"label",
"labelMinor",
"rank"
],
"properties": {
"adjacency": {

View File

@@ -88,11 +88,13 @@ func AddControllers(
client.KubeSphere(),
kubesphereInformer.Devops().V1alpha1().S2iBinaries(),
kubesphereInformer.Devops().V1alpha1().S2iRuns())
devopsProjectController := devopsproject.NewController(client.Kubernetes(),
client.KubeSphere(), devopsClient,
informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(),
informerFactory.KubeSphereSharedInformerFactory().Devops().V1alpha3().DevOpsProjects(),
)
devopsPipelineController := pipeline.NewController(client.Kubernetes(),
client.KubeSphere(),
devopsClient,
@@ -120,9 +122,8 @@ func AddControllers(
clusterController := cluster.NewClusterController(
client.Kubernetes(),
client.Config(),
kubesphereInformer.Cluster().V1alpha1().Clusters(),
kubesphereInformer.Cluster().V1alpha1().Agents(),
client.KubeSphere().ClusterV1alpha1().Agents(),
client.KubeSphere().ClusterV1alpha1().Clusters())
controllers := map[string]manager.Runnable{

View File

@@ -9,6 +9,7 @@ import (
kubesphereconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"strings"
@@ -16,24 +17,28 @@ import (
)
type KubeSphereControllerManagerOptions struct {
KubernetesOptions *k8s.KubernetesOptions
DevopsOptions *jenkins.Options
S3Options *s3.Options
OpenPitrixOptions *openpitrix.Options
LeaderElection *leaderelection.LeaderElectionConfig
KubernetesOptions *k8s.KubernetesOptions
DevopsOptions *jenkins.Options
S3Options *s3.Options
OpenPitrixOptions *openpitrix.Options
MultiClusterOptions *multicluster.Options
LeaderElect bool
LeaderElection *leaderelection.LeaderElectionConfig
}
func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions {
s := &KubeSphereControllerManagerOptions{
KubernetesOptions: k8s.NewKubernetesOptions(),
DevopsOptions: jenkins.NewDevopsOptions(),
S3Options: s3.NewS3Options(),
OpenPitrixOptions: openpitrix.NewOptions(),
KubernetesOptions: k8s.NewKubernetesOptions(),
DevopsOptions: jenkins.NewDevopsOptions(),
S3Options: s3.NewS3Options(),
OpenPitrixOptions: openpitrix.NewOptions(),
MultiClusterOptions: multicluster.NewOptions(),
LeaderElection: &leaderelection.LeaderElectionConfig{
LeaseDuration: 30 * time.Second,
RenewDeadline: 15 * time.Second,
RetryPeriod: 5 * time.Second,
},
LeaderElect: false,
}
return s
@@ -53,10 +58,15 @@ func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets {
s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions)
s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options)
s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions)
s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions)
fs := fss.FlagSet("leaderelection")
s.bindLeaderElectionFlags(s.LeaderElection, fs)
fs.BoolVar(&s.LeaderElect, "leader-elect", s.LeaderElect, ""+
"Whether to enable leader election. This field should be enabled when controller manager"+
"deployed with multiple replicas.")
kfs := fss.FlagSet("klog")
local := flag.NewFlagSet("klog", flag.ExitOnError)
klog.InitFlags(local)

View File

@@ -37,16 +37,17 @@ import (
"kubesphere.io/kubesphere/pkg/controller/namespace"
"kubesphere.io/kubesphere/pkg/controller/user"
"kubesphere.io/kubesphere/pkg/controller/workspace"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/utils/term"
"os"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
func NewControllerManagerCommand() *cobra.Command {
@@ -60,6 +61,7 @@ func NewControllerManagerCommand() *cobra.Command {
S3Options: conf.S3Options,
OpenPitrixOptions: conf.OpenPitrixOptions,
LeaderElection: s.LeaderElection,
LeaderElect: s.LeaderElect,
}
}
@@ -175,6 +177,11 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
cancel()
}()
if !s.LeaderElect {
run(ctx)
return nil
}
id, err := os.Hostname()
if err != nil {
return err

View File

@@ -17,6 +17,7 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
esclient "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
"kubesphere.io/kubesphere/pkg/simple/client/network"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
@@ -52,6 +53,7 @@ func NewServerRunOptions() *ServerRunOptions {
LdapOptions: ldap.NewOptions(),
RedisOptions: cache.NewRedisOptions(),
AuthenticationOptions: authoptions.NewAuthenticateOptions(),
MultiClusterOptions: multicluster.NewOptions(),
},
}
@@ -74,6 +76,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions)
s.MonitoringOptions.AddFlags(fss.FlagSet("monitoring"), s.MonitoringOptions)
s.LoggingOptions.AddFlags(fss.FlagSet("logging"), s.LoggingOptions)
s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions)
fs = fss.FlagSet("klog")
local := flag.NewFlagSet("klog", flag.ExitOnError)

View File

@@ -9,13 +9,13 @@ metadata:
name: clusters.cluster.kubesphere.io
spec:
additionalPrinterColumns:
- JSONPath: .spec.federated
- JSONPath: .spec.joinFederation
name: Federated
type: boolean
- JSONPath: .spec.provider
name: Provider
type: string
- JSONPath: .spec.active
- JSONPath: .spec.enable
name: Active
type: boolean
- JSONPath: .status.kubernetesVersion
@@ -47,10 +47,54 @@ spec:
type: object
spec:
properties:
active:
connection:
description: Connection holds info to connect to the member cluster
properties:
kubeconfig:
description: KubeConfig content used to connect to cluster api server
Should provide this field explicitly if connection type is direct.
Will be populated by ks-proxy if connection type is proxy.
format: byte
type: string
kubernetesAPIEndpoint:
description: Kubernetes API Server endpoint. This can be a hostname,
hostname:port, IP or IP:port. Should provide this field explicitly
if connection type is direct. Will be populated by ks-apiserver
if connection type is proxy.
type: string
kubernetesAPIServerPort:
description: KubeAPIServerPort is the port which listens for forwarding
kube-apiserver traffic Only applicable when connection type is
proxy.
type: integer
kubesphereAPIEndpoint:
description: KubeSphere API Server endpoint. This can be a hostname,
hostname:port, IP or IP:port. Should provide this field explicitly
if connection type is direct. Will be populated by ks-apiserver
if connection type is proxy.
type: string
kubesphereAPIServerPort:
description: KubeSphereAPIServerPort is the port which listens for
forwarding kubesphere apigateway traffic Only applicable when
connection type is proxy.
type: integer
token:
description: Token used by agents of member cluster to connect to
host cluster proxy. This field is populated by apiserver only
if connection type is proxy.
type: string
type:
description: type defines how host cluster will connect to host
cluster ConnectionTypeDirect means direct connection, this requires kubeconfig
and kubesphere apiserver endpoint provided ConnectionTypeProxy
means using kubesphere proxy, no kubeconfig or kubesphere apiserver
endpoint required
type: string
type: object
enable:
description: Desired state of the cluster
type: boolean
federated:
joinFederation:
description: Join cluster as a kubefed cluster
type: boolean
provider:
@@ -92,12 +136,23 @@ spec:
type: object
type: array
kubernetesVersion:
description: GitVersion of the kubernetes cluster, this field is set
description: GitVersion of the kubernetes cluster, this field is populated
by cluster controller
type: string
nodeCount:
description: Count of the kubernetes cluster nodes
description: Count of the kubernetes cluster nodes This field may not
reflect the instant status of the cluster.
type: integer
region:
description: Region is the name of the region in which all of the nodes
in the cluster exist. e.g. 'us-east1'.
type: string
zones:
description: Zones are the names of availability zones in which the
nodes of the cluster exist, e.g. 'us-east1-a'.
items:
type: string
type: array
type: object
type: object
version: v1alpha1

View File

@@ -0,0 +1,59 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: devopsprojects.devops.kubesphere.io
spec:
group: devops.kubesphere.io
names:
categories:
- devops
kind: DevOpsProject
listKind: DevOpsProjectList
plural: devopsprojects
singular: devopsproject
scope: Cluster
validation:
openAPIV3Schema:
description: DevOpsProject is the Schema for the devopsprojects API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: DevOpsProjectSpec defines the desired state of DevOpsProject
type: object
status:
description: DevOpsProjectStatus defines the observed state of DevOpsProject
properties:
adminNamespace:
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
of cluster Important: Run "make" to regenerate code after modifying
this file'
type: string
type: object
type: object
version: v1alpha3
versions:
- name: v1alpha3
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,260 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: pipelines.devops.kubesphere.io
spec:
group: devops.kubesphere.io
names:
kind: Pipeline
listKind: PipelineList
plural: pipelines
singular: pipeline
scope: Namespaced
validation:
openAPIV3Schema:
description: Pipeline is the Schema for the pipelines API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: PipelineSpec defines the desired state of Pipeline
properties:
multi_branch_pipeline:
properties:
bitbucket_server_source:
properties:
api_uri:
type: string
credential_id:
type: string
discover_branches:
type: integer
discover_pr_from_forks:
properties:
strategy:
type: integer
trust:
type: integer
type: object
discover_pr_from_origin:
type: integer
git_clone_option:
properties:
depth:
type: integer
shallow:
type: boolean
timeout:
type: integer
type: object
owner:
type: string
regex_filter:
type: string
repo:
type: string
scm_id:
type: string
type: object
descriptio:
type: string
discarder:
properties:
days_to_keep:
type: string
num_to_keep:
type: string
type: object
git_source:
properties:
credential_id:
type: string
discover_branches:
type: boolean
git_clone_option:
properties:
depth:
type: integer
shallow:
type: boolean
timeout:
type: integer
type: object
regex_filter:
type: string
scm_id:
type: string
url:
type: string
type: object
github_source:
properties:
api_uri:
type: string
credential_id:
type: string
discover_branches:
type: integer
discover_pr_from_forks:
properties:
strategy:
type: integer
trust:
type: integer
type: object
discover_pr_from_origin:
type: integer
git_clone_option:
properties:
depth:
type: integer
shallow:
type: boolean
timeout:
type: integer
type: object
owner:
type: string
regex_filter:
type: string
repo:
type: string
scm_id:
type: string
type: object
multibranch_job_trigger:
properties:
create_action_job_to_trigger:
type: string
delete_action_job_to_trigger:
type: string
type: object
name:
type: string
script_path:
type: string
single_svn_source:
properties:
credential_id:
type: string
remote:
type: string
scm_id:
type: string
type: object
source_type:
type: string
svn_source:
properties:
credential_id:
type: string
excludes:
type: string
includes:
type: string
remote:
type: string
scm_id:
type: string
type: object
timer_trigger:
properties:
cron:
description: user in no scm job
type: string
interval:
description: use in multi-branch job
type: string
type: object
required:
- name
- script_path
- source_type
type: object
pipeline:
properties:
descriptio:
type: string
disable_concurrent:
type: boolean
discarder:
properties:
days_to_keep:
type: string
num_to_keep:
type: string
type: object
jenkinsfile:
type: string
name:
type: string
parameters:
items:
properties:
default_value:
type: string
description:
type: string
name:
type: string
type:
type: string
required:
- name
- type
type: object
type: array
remote_trigger:
properties:
token:
type: string
type: object
timer_trigger:
properties:
cron:
description: user in no scm job
type: string
interval:
description: use in multi-branch job
type: string
type: object
required:
- name
type: object
type:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Important: Run "make" to regenerate code after modifying this file'
type: string
required:
- type
type: object
status:
description: PipelineStatus defines the observed state of Pipeline
type: object
type: object
version: v1alpha3
versions:
- name: v1alpha3
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,58 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: policyrules.iam.kubesphere.io
spec:
additionalPrinterColumns:
- JSONPath: .scope
name: Scope
type: string
group: iam.kubesphere.io
names:
categories:
- iam
kind: PolicyRule
listKind: PolicyRuleList
plural: policyrules
singular: policyrule
scope: Cluster
subresources: {}
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/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
rego:
type: string
scope:
type: string
required:
- rego
- scope
type: object
version: v1alpha2
versions:
- name: v1alpha2
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,104 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: rolebindings.iam.kubesphere.io
spec:
additionalPrinterColumns:
- JSONPath: .scope
name: Scope
type: string
- JSONPath: .roleRef.name
name: RoleRef
type: string
- JSONPath: .subjects[*].name
name: Subjects
type: string
group: iam.kubesphere.io
names:
categories:
- iam
kind: RoleBinding
listKind: RoleBindingList
plural: rolebindings
singular: rolebinding
scope: Cluster
subresources: {}
validation:
openAPIV3Schema:
description: RoleBinding is the Schema for the rolebindings API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
roleRef:
description: RoleRef contains information that points to the role being
used
properties:
apiGroup:
description: APIGroup is the group for the resource being referenced
type: string
kind:
description: Kind is the type of resource being referenced
type: string
name:
description: Name is the name of resource being referenced
type: string
required:
- apiGroup
- kind
- name
type: object
scope:
type: string
subjects:
description: Subjects holds references to the users the role applies to.
items:
description: or a value for non-objects such as user and group names.
properties:
apiGroup:
description: APIGroup holds the API group of the referenced subject.
type: string
kind:
description: Kind of object being referenced. Values defined by this
API group are "User", "Group", and "ServiceAccount". If the Authorizer
does not recognized the kind value, the Authorizer should report
an error.
type: string
name:
description: Name of the object being referenced.
type: string
required:
- apiGroup
- kind
- name
type: object
type: array
required:
- roleRef
- scope
type: object
version: v1alpha2
versions:
- name: v1alpha2
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -0,0 +1,87 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: roles.iam.kubesphere.io
spec:
additionalPrinterColumns:
- JSONPath: .target.scope
name: Scope
type: string
- JSONPath: .target.name
name: Target
type: string
group: iam.kubesphere.io
names:
categories:
- iam
kind: Role
listKind: RoleList
plural: roles
singular: role
scope: Cluster
subresources: {}
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/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
rules:
items:
description: RuleRef contains information that points to the role being
used
properties:
apiGroup:
description: APIGroup is the group for the resource being referenced
type: string
kind:
description: Kind is the type of resource being referenced
type: string
name:
description: Name is the name of resource being referenced
type: string
required:
- apiGroup
- kind
- name
type: object
type: array
target:
properties:
name:
type: string
scope:
type: string
required:
- name
- scope
type: object
required:
- rules
- target
type: object
version: v1alpha2
versions:
- name: v1alpha2
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

52
go.mod
View File

@@ -32,7 +32,6 @@ require (
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1 // indirect
github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-openapi/loads v0.19.2
github.com/go-openapi/spec v0.19.3
github.com/go-openapi/strfmt v0.19.0
@@ -52,26 +51,25 @@ require (
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/go-version v1.2.0 // indirect
github.com/imdario/mergo v0.3.7 // indirect
github.com/json-iterator/go v1.1.8
github.com/json-iterator/go v1.1.9
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kiali/kiali v0.15.1-0.20191210080139-edbbad1ef779
github.com/kubernetes-sigs/application v0.0.0-20191210100950-18cc93526ab4
github.com/kubesphere/sonargo v0.0.2
github.com/leodido/go-urn v1.1.0 // indirect
github.com/lib/pq v1.2.0 // indirect
github.com/mailru/easyjson v0.7.0 // indirect
github.com/mattn/go-sqlite3 v1.11.0 // indirect
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
github.com/onsi/ginkgo v1.12.0
github.com/onsi/gomega v1.9.0
github.com/open-policy-agent/opa v0.18.0
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/openshift/api v0.0.0-20180801171038-322a19404e37 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.1
github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce
github.com/prometheus/client_golang v0.9.4
github.com/prometheus/client_golang v1.0.0
github.com/prometheus/common v0.4.1
github.com/prometheus/prometheus v1.8.2
github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
@@ -89,25 +87,25 @@ require (
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
gopkg.in/src-d/go-git.v4 v4.11.0
gopkg.in/yaml.v2 v2.2.4
gopkg.in/yaml.v2 v2.2.8
istio.io/api v0.0.0-20191111210003-35e06ef8d838
istio.io/client-go v0.0.0-20191113122552-9bd0ba57c3d2
k8s.io/api v0.0.0-20191114100352-16d7abae0d2a
k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833
k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb
k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682
k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894
k8s.io/component-base v0.0.0-20191114102325-35a9586014f7
k8s.io/api v0.17.3
k8s.io/apiextensions-apiserver v0.17.3
k8s.io/apimachinery v0.17.3
k8s.io/apiserver v0.17.3
k8s.io/client-go v0.17.3
k8s.io/code-generator v0.17.3
k8s.io/component-base v0.17.3
k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e // indirect
k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f // indirect
kubesphere.io/im v0.1.0 // indirect
openpitrix.io/iam v0.1.0 // indirect
openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c
sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-runtime v0.5.0
sigs.k8s.io/controller-tools v0.2.4
sigs.k8s.io/kubefed v0.2.0-alpha.1
)
replace (
@@ -121,6 +119,7 @@ replace (
github.com/Azure/go-autorest/logger => github.com/Azure/go-autorest/logger v0.1.0
github.com/Azure/go-autorest/tracing => github.com/Azure/go-autorest/tracing v0.5.0
github.com/BurntSushi/toml => github.com/BurntSushi/toml v0.3.1
github.com/MakeNowJust/heredoc => github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd
github.com/Masterminds/semver => github.com/Masterminds/semver v1.5.0
github.com/Microsoft/go-winio => github.com/Microsoft/go-winio v0.4.12
github.com/NYTimes/gziphandler => github.com/NYTimes/gziphandler v1.1.1
@@ -144,6 +143,7 @@ replace (
github.com/bmizerany/assert => github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/cenkalti/backoff => github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cespare/xxhash => github.com/cespare/xxhash v1.1.0
github.com/chai2010/gettext-go => github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5
github.com/cheekybits/genny => github.com/cheekybits/genny v1.0.0
github.com/client9/misspell => github.com/client9/misspell v0.3.4
github.com/coreos/bbolt => github.com/coreos/bbolt v1.3.3
@@ -154,6 +154,7 @@ replace (
github.com/coreos/pkg => github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f
github.com/cpuguy83/go-md2man => github.com/cpuguy83/go-md2man v1.0.10
github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.1
github.com/daviddengcn/go-colortext => github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd
github.com/deckarep/golang-set => github.com/deckarep/golang-set v1.7.1
github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289
github.com/dgrijalva/jwt-go => github.com/dgrijalva/jwt-go v3.2.0+incompatible
@@ -175,6 +176,7 @@ replace (
github.com/emirpasic/gods => github.com/emirpasic/gods v1.12.0
github.com/erikstmartin/go-testdb => github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
github.com/evanphx/json-patch => github.com/evanphx/json-patch v4.5.0+incompatible
github.com/exponent-io/jsonpath => github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d
github.com/fatih/camelcase => github.com/fatih/camelcase v1.0.0
github.com/fatih/color => github.com/fatih/color v1.7.0
github.com/fatih/structs => github.com/fatih/structs v1.1.0
@@ -216,6 +218,9 @@ replace (
github.com/golang/mock => github.com/golang/mock v1.2.0
github.com/golang/protobuf => github.com/golang/protobuf v1.3.2
github.com/golang/snappy => github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db
github.com/golangplus/bytes => github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450
github.com/golangplus/fmt => github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995
github.com/golangplus/testing => github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e
github.com/google/btree => github.com/google/btree v1.0.0
github.com/google/go-cmp => github.com/google/go-cmp v0.3.0
github.com/google/go-querystring => github.com/google/go-querystring v1.0.0
@@ -266,11 +271,14 @@ replace (
github.com/kr/pty => github.com/kr/pty v1.1.5
github.com/kr/text => github.com/kr/text v0.1.0
github.com/kubernetes-sigs/application => github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4
github.com/kubernetes-sigs/federation-v2 => github.com/kubernetes-sigs/federation-v2 v0.0.10
github.com/kubesphere/s2ioperator => github.com/kubesphere/s2ioperator v0.0.14
github.com/kubesphere/sonargo => github.com/kubesphere/sonargo v0.0.2
github.com/kylelemons/godebug => github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
github.com/leodido/go-urn => github.com/leodido/go-urn v1.1.0
github.com/lib/pq => github.com/lib/pq v1.2.0
github.com/liggitt/tabwriter => github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/lithammer/dedent => github.com/lithammer/dedent v1.1.0
github.com/lucas-clemente/quic-go => github.com/lucas-clemente/quic-go v0.11.1
github.com/magiconair/properties => github.com/magiconair/properties v1.8.0
github.com/mailru/easyjson => github.com/mailru/easyjson v0.7.0
@@ -284,6 +292,7 @@ replace (
github.com/mholt/certmagic => github.com/mholt/certmagic v0.5.1
github.com/miekg/dns => github.com/miekg/dns v1.1.9
github.com/mitchellh/go-homedir => github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-wordwrap => github.com/mitchellh/go-wordwrap v1.0.0
github.com/mitchellh/mapstructure => github.com/mitchellh/mapstructure v1.1.2
github.com/mna/pigeon => github.com/mna/pigeon v0.0.0-20180808201053-bb0192cfc2ae
github.com/modern-go/concurrent => github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
@@ -303,6 +312,7 @@ replace (
github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.1
github.com/openshift/api => github.com/openshift/api v0.0.0-20180801171038-322a19404e37
github.com/openshift/build-machinery-go => github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160
github.com/openshift/generic-admission-server => github.com/openshift/generic-admission-server v1.14.0
github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0
github.com/pborman/uuid => github.com/pborman/uuid v1.2.0
github.com/pelletier/go-buffruneio => github.com/pelletier/go-buffruneio v0.2.0
@@ -354,6 +364,7 @@ replace (
github.com/urfave/cli => github.com/urfave/cli v1.20.0
github.com/xanzy/ssh-agent => github.com/xanzy/ssh-agent v0.2.1
github.com/xiang90/probing => github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2
github.com/xlab/handysort => github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1
github.com/xlab/treeprint => github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6
github.com/xordataexchange/crypt => github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77
github.com/yashtewari/glob-intersection => github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b
@@ -411,12 +422,16 @@ replace (
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb
k8s.io/apiserver => k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682
k8s.io/cli-runtime => k8s.io/cli-runtime v0.17.3
k8s.io/client-go => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
k8s.io/cluster-registry => k8s.io/cluster-registry v0.0.6
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894
k8s.io/component-base => k8s.io/component-base v0.0.0-20191114102325-35a9586014f7
k8s.io/gengo => k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e
k8s.io/klog => k8s.io/klog v1.0.0
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/kubectl => k8s.io/kubectl v0.17.3
k8s.io/metrics => k8s.io/metrics v0.17.3
k8s.io/utils => k8s.io/utils v0.0.0-20191114184206-e782cd3c129f
kubesphere.io/application => kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e
kubesphere.io/im => kubesphere.io/im v0.1.0
@@ -431,7 +446,10 @@ replace (
rsc.io/goversion => rsc.io/goversion v1.0.0
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.2.4
sigs.k8s.io/kubefed => sigs.k8s.io/kubefed v0.2.0-alpha.1
sigs.k8s.io/kustomize => sigs.k8s.io/kustomize v2.0.3+incompatible
sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca
sigs.k8s.io/testing_frameworks => sigs.k8s.io/testing_frameworks v0.1.2
sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0
vbom.ml/util => vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc
)

25
go.sum
View File

@@ -18,6 +18,7 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
@@ -60,6 +61,7 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -75,6 +77,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
@@ -110,6 +113,7 @@ github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
@@ -126,11 +130,9 @@ github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
@@ -165,7 +167,6 @@ github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDA
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo=
github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
@@ -188,6 +189,9 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
@@ -210,7 +214,6 @@ github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -256,7 +259,6 @@ github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 h1:SWlt7BoQNASb
github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -273,6 +275,8 @@ github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
@@ -288,6 +292,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mna/pigeon v0.0.0-20180808201053-bb0192cfc2ae/go.mod h1:Iym28+kJVnC1hfQvv5MUtI6AiFFzvQjHcvI4RFTG/04=
@@ -315,6 +320,7 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/openshift/api v0.0.0-20180801171038-322a19404e37 h1:05irGU4HK4IauGGDbsk+ZHrm1wOzMLYjMlfaiqMrBYc=
github.com/openshift/api v0.0.0-20180801171038-322a19404e37/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
github.com/openshift/generic-admission-server v1.14.0/go.mod h1:GD9KN/W4KxqRQGVMbqQHpHzb2XcQVvLCaBaSciqXvfM=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
@@ -401,6 +407,7 @@ github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b h1:vVRagRXf67ESqAb72hG2C/ZwI8NtJF2u2V76EsuOHGY=
@@ -497,6 +504,7 @@ k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWc
k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 h1:+FvAOv/4JyYgZanQI8h+UW9FCmLzyEz7EZunuET6p5g=
k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682/go.mod h1:Idob8Va6/sMX5SmwPLsU0pdvFlkwxuJ5x+fXMG8NbKE=
k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA=
k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ=
k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw=
k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 h1:NMYlxaF7rYQJk2E2IyrUhaX81zX24+dmoZdkPw0gJqI=
@@ -509,6 +517,9 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kubectl v0.17.3 h1:9HHYj07kuFkM+sMJMOyQX29CKWq4lvKAG1UIPxNPMQ4=
k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28=
k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e/go.mod h1:NhUQ0ZUdFz8NTQ+SvQG0JUKAn+q71v3TPExjsjRPIZI=
@@ -529,9 +540,13 @@ sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/controller-tools v0.2.4 h1:la1h46EzElvWefWLqfsXrnsO3lZjpkI0asTpX6h8PLA=
sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA=
sigs.k8s.io/kubefed v0.2.0-alpha.1 h1:nzaQ4HDReHLECXMv7iszHBLx3+GO3/Iwlw7dkS71qCw=
sigs.k8s.io/kubefed v0.2.0-alpha.1/go.mod h1:/X4yMEvaclI6CAeVwFBjtGJ1E3gwXcuVwNbGPXPz+CM=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=

View File

@@ -1,123 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
ResourceKindAgent = "Agent"
ResourcesSingularAgent = "agent"
ResourcesPluralAgent = "agents"
)
// 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.
// AgentSpec defines the desired state of Agent
type AgentSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// Token used by agents to connect to proxy.
// +optional
Token string `json:"token,omitempty"`
// Proxy address
// +optional
Proxy string `json:"proxy,omitempty"`
// KubeAPIServerPort is the port which listens for forwarding kube-apiserver traffic
// +optional
KubernetesAPIServerPort uint16 `json:"kubernetesAPIServerPort,omitempty"`
// KubeSphereAPIServerPort is the port which listens for forwarding kubesphere apigateway traffic
// +optional
KubeSphereAPIServerPort uint16 `json:"kubesphereAPIServerPort,omitempty"`
// Indicates that the agent is paused.
// +optional
Paused bool `json:"paused,omitempty"`
}
type AgentConditionType string
const (
// Agent is initialized, and waiting for establishing to a proxy server
AgentInitialized AgentConditionType = "Initialized"
// Agent has successfully connected to proxy server
AgentConnected AgentConditionType = "Connected"
)
type AgentCondition struct {
// Type of AgentCondition
Type AgentConditionType `json:"type,omitempty"`
// Status of the condition, one of True, False, Unknown.
Status v1.ConditionStatus `json:"status"`
// The last time this condition was updated.
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
// Last time the condition transitioned from one status to another.
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
// The reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// A human readable message indicating details about the transition.
Message string `json:"message,omitempty"`
}
// AgentStatus defines the observed state of Agent
type AgentStatus struct {
// Represents the latest available observations of a agent's current state.
Conditions []AgentCondition `json:"conditions,omitempty"`
// Represents the connection quality, in ms
Ping uint64 `json:"ping,omitempty"`
// Issued new kubeconfig by proxy server
KubeConfig []byte `json:"kubeconfig,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
// +genclient:nonNamespaced
// +kubebuilder:printcolumn:name="Paused",type="bool",JSONPath=".spec.Paused"
// +kubebuilder:resource:scope=Cluster
// Agent is the Schema for the agents API
type Agent struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec AgentSpec `json:"spec,omitempty"`
Status AgentStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// AgentList contains a list of Agent
type AgentList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Agent `json:"items"`
}
func init() {
SchemeBuilder.Register(&Agent{}, &AgentList{})
}

View File

@@ -11,20 +11,71 @@ const (
ResourcesPluralCluster = "clusters"
IsHostCluster = "cluster.kubesphere.io/is-host-cluster"
// Description of which region the cluster been placed
ClusterRegion = "cluster.kubesphere.io/region"
// Name of the cluster group
ClusterGroup = "cluster.kubesphere.io/group"
Finalizer = "finalizer.cluster.kubesphere.io"
)
type ClusterSpec struct {
// Join cluster as a kubefed cluster
// +optional
Federated bool `json:"federated,omitempty"`
JoinFederation bool `json:"joinFederation,omitempty"`
// Desired state of the cluster
Active bool `json:"active,omitempty"`
Enable bool `json:"enable,omitempty"`
// Provider of the cluster, this field is just for description
// +optional
Provider string `json:"provider,omitempty"`
// Connection holds info to connect to the member cluster
Connection Connection `json:"connection,omitempty"`
}
type ConnectionType string
const (
ConnectionTypeDirect ConnectionType = "direct"
ConnectionTypeProxy ConnectionType = "proxy"
)
type Connection struct {
// type defines how host cluster will connect to host cluster
// ConnectionTypeDirect means direct connection, this requires
// kubeconfig and kubesphere apiserver endpoint provided
// ConnectionTypeProxy means using kubesphere proxy, no kubeconfig
// or kubesphere apiserver endpoint required
Type ConnectionType `json:"type,omitempty"`
// KubeSphere API Server endpoint. Example: http://10.10.0.11:8080
// Should provide this field explicitly if connection type is direct.
// Will be populated by ks-apiserver if connection type is proxy.
KubeSphereAPIEndpoint string `json:"kubesphereAPIEndpoint,omitempty"`
// Kubernetes API Server endpoint. Example: https://10.10.0.1:6443
// Should provide this field explicitly if connection type is direct.
// Will be populated by ks-apiserver if connection type is proxy.
KubernetesAPIEndpoint string `json:"kubernetesAPIEndpoint,omitempty"`
// KubeConfig content used to connect to cluster api server
// Should provide this field explicitly if connection type is direct.
// Will be populated by ks-proxy if connection type is proxy.
KubeConfig []byte `json:"kubeconfig,omitempty"`
// Token used by agents of member cluster to connect to host cluster proxy.
// This field is populated by apiserver only if connection type is proxy.
Token string `json:"token,omitempty"`
// KubeAPIServerPort is the port which listens for forwarding kube-apiserver traffic
// Only applicable when connection type is proxy.
KubernetesAPIServerPort uint16 `json:"kubernetesAPIServerPort,omitempty"`
// KubeSphereAPIServerPort is the port which listens for forwarding kubesphere apigateway traffic
// Only applicable when connection type is proxy.
KubeSphereAPIServerPort uint16 `json:"kubesphereAPIServerPort,omitempty"`
}
type ClusterConditionType string
@@ -38,6 +89,9 @@ const (
// Cluster has been one of federated clusters
ClusterFederated ClusterConditionType = "Federated"
// Cluster is all available for requests
ClusterReady ClusterConditionType = "Ready"
)
type ClusterCondition struct {
@@ -60,22 +114,29 @@ type ClusterStatus struct {
// Represents the latest available observations of a cluster's current state.
Conditions []ClusterCondition `json:"conditions,omitempty"`
// GitVersion of the kubernetes cluster, this field is set by cluster controller
// +optional
// GitVersion of the kubernetes cluster, this field is populated by cluster controller
KubernetesVersion string `json:"kubernetesVersion,omitempty"`
// Count of the kubernetes cluster nodes
// +optional
// This field may not reflect the instant status of the cluster.
NodeCount int `json:"nodeCount,omitempty"`
// Zones are the names of availability zones in which the nodes of the cluster exist, e.g. 'us-east1-a'.
// +optional
Zones []string `json:"zones,omitempty"`
// Region is the name of the region in which all of the nodes in the cluster exist. e.g. 'us-east1'.
// +optional
Region *string `json:"region,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
// +genclient:nonNamespaced
// +kubebuilder:printcolumn:name="Federated",type="boolean",JSONPath=".spec.federated"
// +kubebuilder:printcolumn:name="Federated",type="boolean",JSONPath=".spec.joinFederation"
// +kubebuilder:printcolumn:name="Provider",type="string",JSONPath=".spec.provider"
// +kubebuilder:printcolumn:name="Active",type="boolean",JSONPath=".spec.active"
// +kubebuilder:printcolumn:name="Active",type="boolean",JSONPath=".spec.enable"
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.kubernetesVersion"
// +kubebuilder:resource:scope=Cluster

View File

@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
@@ -24,137 +24,13 @@ 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 *Agent) DeepCopyInto(out *Agent) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Agent.
func (in *Agent) DeepCopy() *Agent {
if in == nil {
return nil
}
out := new(Agent)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Agent) 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 *AgentCondition) DeepCopyInto(out *AgentCondition) {
*out = *in
in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime)
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentCondition.
func (in *AgentCondition) DeepCopy() *AgentCondition {
if in == nil {
return nil
}
out := new(AgentCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AgentList) DeepCopyInto(out *AgentList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Agent, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentList.
func (in *AgentList) DeepCopy() *AgentList {
if in == nil {
return nil
}
out := new(AgentList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *AgentList) 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 *AgentSpec) DeepCopyInto(out *AgentSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentSpec.
func (in *AgentSpec) DeepCopy() *AgentSpec {
if in == nil {
return nil
}
out := new(AgentSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AgentStatus) DeepCopyInto(out *AgentStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]AgentCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig
*out = make([]byte, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentStatus.
func (in *AgentStatus) DeepCopy() *AgentStatus {
if in == nil {
return nil
}
out := new(AgentStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Cluster) DeepCopyInto(out *Cluster) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
@@ -180,7 +56,6 @@ func (in *ClusterCondition) DeepCopyInto(out *ClusterCondition) {
*out = *in
in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime)
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCondition.
@@ -205,7 +80,6 @@ func (in *ClusterList) DeepCopyInto(out *ClusterList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterList.
@@ -229,7 +103,7 @@ func (in *ClusterList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) {
*out = *in
return
in.Connection.DeepCopyInto(&out.Connection)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSpec.
@@ -252,7 +126,16 @@ func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
if in.Zones != nil {
in, out := &in.Zones, &out.Zones
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Region != nil {
in, out := &in.Region, &out.Region
*out = new(string)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterStatus.
@@ -264,3 +147,23 @@ func (in *ClusterStatus) DeepCopy() *ClusterStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Connection) DeepCopyInto(out *Connection) {
*out = *in
if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig
*out = make([]byte, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Connection.
func (in *Connection) DeepCopy() *Connection {
if in == nil {
return nil
}
out := new(Connection)
in.DeepCopyInto(out)
return out
}

View File

@@ -16,12 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -33,7 +33,6 @@ func (in *AuthConfig) DeepCopyInto(out *AuthConfig) {
*out = new(v1.LocalObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig.
@@ -49,7 +48,6 @@ func (in *AuthConfig) DeepCopy() *AuthConfig {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CGroupLimits) DeepCopyInto(out *CGroupLimits) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CGroupLimits.
@@ -77,7 +75,6 @@ func (in *ContainerConfig) DeepCopyInto(out *ContainerConfig) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerConfig.
@@ -103,7 +100,6 @@ func (in *ContainerInfo) DeepCopyInto(out *ContainerInfo) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerInfo.
@@ -119,7 +115,6 @@ func (in *ContainerInfo) DeepCopy() *ContainerInfo {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DockerConfig) DeepCopyInto(out *DockerConfig) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfig.
@@ -135,7 +130,6 @@ func (in *DockerConfig) DeepCopy() *DockerConfig {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DockerConfigEntry) DeepCopyInto(out *DockerConfigEntry) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigEntry.
@@ -158,7 +152,6 @@ func (in *DockerConfigJson) DeepCopyInto(out *DockerConfigJson) {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigJson.
@@ -179,7 +172,6 @@ func (in DockerConfigMap) DeepCopyInto(out *DockerConfigMap) {
for key, val := range *in {
(*out)[key] = val
}
return
}
}
@@ -196,7 +188,6 @@ func (in DockerConfigMap) DeepCopy() DockerConfigMap {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EnvironmentSpec) DeepCopyInto(out *EnvironmentSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentSpec.
@@ -217,7 +208,6 @@ func (in *Parameter) DeepCopyInto(out *Parameter) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter.
@@ -233,7 +223,6 @@ func (in *Parameter) DeepCopy() *Parameter {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProxyConfig) DeepCopyInto(out *ProxyConfig) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfig.
@@ -259,7 +248,6 @@ func (in *S2iAutoScale) DeepCopyInto(out *S2iAutoScale) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iAutoScale.
@@ -279,7 +267,6 @@ func (in *S2iBinary) DeepCopyInto(out *S2iBinary) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinary.
@@ -312,7 +299,6 @@ func (in *S2iBinaryList) DeepCopyInto(out *S2iBinaryList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinaryList.
@@ -340,7 +326,6 @@ func (in *S2iBinarySpec) DeepCopyInto(out *S2iBinarySpec) {
in, out := &in.UploadTimeStamp, &out.UploadTimeStamp
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinarySpec.
@@ -356,7 +341,6 @@ func (in *S2iBinarySpec) DeepCopy() *S2iBinarySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S2iBinaryStatus) DeepCopyInto(out *S2iBinaryStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinaryStatus.
@@ -377,7 +361,6 @@ func (in *S2iBuildResult) DeepCopyInto(out *S2iBuildResult) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuildResult.
@@ -393,7 +376,6 @@ func (in *S2iBuildResult) DeepCopy() *S2iBuildResult {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S2iBuildSource) DeepCopyInto(out *S2iBuildSource) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuildSource.
@@ -413,7 +395,6 @@ func (in *S2iBuilder) DeepCopyInto(out *S2iBuilder) {
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 S2iBuilder.
@@ -446,7 +427,6 @@ func (in *S2iBuilderList) DeepCopyInto(out *S2iBuilderList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderList.
@@ -480,7 +460,6 @@ func (in *S2iBuilderSpec) DeepCopyInto(out *S2iBuilderSpec) {
*out = new(UserDefineTemplate)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderSpec.
@@ -505,7 +484,6 @@ func (in *S2iBuilderStatus) DeepCopyInto(out *S2iBuilderStatus) {
in, out := &in.LastRunStartTime, &out.LastRunStartTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderStatus.
@@ -525,7 +503,6 @@ func (in *S2iBuilderTemplate) DeepCopyInto(out *S2iBuilderTemplate) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplate.
@@ -558,7 +535,6 @@ func (in *S2iBuilderTemplateList) DeepCopyInto(out *S2iBuilderTemplateList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateList.
@@ -596,7 +572,6 @@ func (in *S2iBuilderTemplateSpec) DeepCopyInto(out *S2iBuilderTemplateSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateSpec.
@@ -612,7 +587,6 @@ func (in *S2iBuilderTemplateSpec) DeepCopy() *S2iBuilderTemplateSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S2iBuilderTemplateStatus) DeepCopyInto(out *S2iBuilderTemplateStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateStatus.
@@ -715,7 +689,6 @@ func (in *S2iConfig) DeepCopyInto(out *S2iConfig) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iConfig.
@@ -735,7 +708,6 @@ func (in *S2iRun) DeepCopyInto(out *S2iRun) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRun.
@@ -768,7 +740,6 @@ func (in *S2iRunList) DeepCopyInto(out *S2iRunList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunList.
@@ -792,7 +763,6 @@ func (in *S2iRunList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S2iRunSpec) DeepCopyInto(out *S2iRunSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunSpec.
@@ -826,7 +796,6 @@ func (in *S2iRunStatus) DeepCopyInto(out *S2iRunStatus) {
*out = new(S2iBuildSource)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunStatus.
@@ -849,7 +818,6 @@ func (in *UserDefineTemplate) DeepCopyInto(out *UserDefineTemplate) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserDefineTemplate.
@@ -865,7 +833,6 @@ func (in *UserDefineTemplate) DeepCopy() *UserDefineTemplate {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSpec.

View File

@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha2
@@ -274,7 +274,6 @@ func (in *User) DeepCopyInto(out *User) {
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 User.
@@ -299,7 +298,6 @@ func (in *User) DeepCopyObject() runtime.Object {
func (in *UserCondition) DeepCopyInto(out *UserCondition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserCondition.
@@ -349,7 +347,6 @@ func (in *UserList) DeepCopyInto(out *UserList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserList.
@@ -383,7 +380,6 @@ func (in *UserSpec) DeepCopyInto(out *UserSpec) {
*out = make([]FinalizerName, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec.
@@ -406,7 +402,6 @@ func (in *UserStatus) DeepCopyInto(out *UserStatus) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserStatus.

View File

@@ -16,16 +16,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
numorstring "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@@ -56,7 +56,6 @@ func (in *EntityRule) DeepCopyInto(out *EntityRule) {
*out = new(ServiceAccountMatch)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EntityRule.
@@ -82,7 +81,6 @@ func (in *HTTPMatch) DeepCopyInto(out *HTTPMatch) {
*out = make([]HTTPPath, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatch.
@@ -98,7 +96,6 @@ func (in *HTTPMatch) DeepCopy() *HTTPMatch {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPPath) DeepCopyInto(out *HTTPPath) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPath.
@@ -124,7 +121,6 @@ func (in *ICMPFields) DeepCopyInto(out *ICMPFields) {
*out = new(int)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ICMPFields.
@@ -143,7 +139,6 @@ func (in *NamespaceNetworkPolicy) DeepCopyInto(out *NamespaceNetworkPolicy) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicy.
@@ -176,7 +171,6 @@ func (in *NamespaceNetworkPolicyList) DeepCopyInto(out *NamespaceNetworkPolicyLi
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicyList.
@@ -224,7 +218,6 @@ func (in *NamespaceNetworkPolicySpec) DeepCopyInto(out *NamespaceNetworkPolicySp
*out = make([]PolicyType, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicySpec.
@@ -272,7 +265,6 @@ func (in *Rule) DeepCopyInto(out *Rule) {
*out = new(HTTPMatch)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
@@ -293,7 +285,6 @@ func (in *ServiceAccountMatch) DeepCopyInto(out *ServiceAccountMatch) {
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountMatch.
@@ -313,7 +304,6 @@ func (in *WorkspaceNetworkPolicy) DeepCopyInto(out *WorkspaceNetworkPolicy) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicy.
@@ -351,7 +341,6 @@ func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPo
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyEgressRule.
@@ -381,7 +370,6 @@ func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkP
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyIngressRule.
@@ -406,7 +394,6 @@ func (in *WorkspaceNetworkPolicyList) DeepCopyInto(out *WorkspaceNetworkPolicyLi
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyList.
@@ -436,7 +423,6 @@ func (in *WorkspaceNetworkPolicyPeer) DeepCopyInto(out *WorkspaceNetworkPolicyPe
*out = new(metav1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyPeer.
@@ -471,7 +457,6 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySp
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicySpec.
@@ -487,7 +472,6 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopy() *WorkspaceNetworkPolicySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyStatus) DeepCopyInto(out *WorkspaceNetworkPolicyStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyStatus.

View File

@@ -16,12 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha2
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -30,7 +30,6 @@ func (in *DestinationRuleSpecTemplate) DeepCopyInto(out *DestinationRuleSpecTemp
*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.
@@ -50,7 +49,6 @@ func (in *ServicePolicy) DeepCopyInto(out *ServicePolicy) {
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.
@@ -76,7 +74,6 @@ 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.
@@ -101,7 +98,6 @@ func (in *ServicePolicyList) DeepCopyInto(out *ServicePolicyList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyList.
@@ -131,7 +127,6 @@ func (in *ServicePolicySpec) DeepCopyInto(out *ServicePolicySpec) {
(*in).DeepCopyInto(*out)
}
in.Template.DeepCopyInto(&out.Template)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicySpec.
@@ -162,7 +157,6 @@ func (in *ServicePolicyStatus) DeepCopyInto(out *ServicePolicyStatus) {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyStatus.
@@ -182,7 +176,6 @@ func (in *Strategy) DeepCopyInto(out *Strategy) {
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 Strategy.
@@ -208,7 +201,6 @@ func (in *StrategyCondition) DeepCopyInto(out *StrategyCondition) {
*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 StrategyCondition.
@@ -233,7 +225,6 @@ func (in *StrategyList) DeepCopyInto(out *StrategyList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyList.
@@ -263,7 +254,6 @@ func (in *StrategySpec) DeepCopyInto(out *StrategySpec) {
(*in).DeepCopyInto(*out)
}
in.Template.DeepCopyInto(&out.Template)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategySpec.
@@ -294,7 +284,6 @@ func (in *StrategyStatus) DeepCopyInto(out *StrategyStatus) {
in, out := &in.CompletionTime, &out.CompletionTime
*out = (*in).DeepCopy()
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyStatus.
@@ -312,7 +301,6 @@ func (in *VirtualServiceTemplateSpec) DeepCopyInto(out *VirtualServiceTemplateSp
*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 VirtualServiceTemplateSpec.

View File

@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
@@ -31,7 +31,6 @@ func (in *Workspace) DeepCopyInto(out *Workspace) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workspace.
@@ -64,7 +63,6 @@ func (in *WorkspaceList) DeepCopyInto(out *WorkspaceList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceList.
@@ -88,7 +86,6 @@ func (in *WorkspaceList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceSpec) DeepCopyInto(out *WorkspaceSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceSpec.
@@ -104,7 +101,6 @@ func (in *WorkspaceSpec) DeepCopy() *WorkspaceSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceStatus) DeepCopyInto(out *WorkspaceStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceStatus.

View File

@@ -94,7 +94,7 @@ type APIServer struct {
// monitoring client set
MonitoringClient monitoring.Interface
//
// openpitrix client
OpenpitrixClient openpitrix.Client
//
@@ -188,8 +188,10 @@ func (s *APIServer) buildHandlerChain() {
handler := s.Server.Handler
handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{})
clusterDispatcher := dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Agents().Lister(), s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister())
handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher)
if s.Config.MultiClusterOptions.Enable {
clusterDispatcher := dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister())
handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher)
}
excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"}
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
@@ -284,6 +286,7 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error {
{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "roles"},
{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "rolebindings"},
{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "policyrules"},
{Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"},
}
devopsGVRs := []schema.GroupVersionResource{
@@ -332,7 +335,7 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error {
if !isResourceExists(gvr) {
klog.Warningf("resource %s not exists in the cluster", gvr)
} else {
_, err := appInformerFactory.ForResource(gvr)
_, err = appInformerFactory.ForResource(gvr)
if err != nil {
return err
}

View File

@@ -11,6 +11,7 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
"kubesphere.io/kubesphere/pkg/simple/client/network"
"kubesphere.io/kubesphere/pkg/simple/client/notification"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
@@ -63,13 +64,14 @@ type Config struct {
KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"`
ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"`
NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"`
LdapOptions *ldap.Options `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"`
RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"`
LdapOptions *ldap.Options `json:"-" yaml:"ldap,omitempty" mapstructure:"ldap"`
RedisOptions *cache.Options `json:"-" yaml:"redis,omitempty" mapstructure:"redis"`
S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"`
OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"`
MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"`
LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"`
AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"`
AuthenticationOptions *authoptions.AuthenticationOptions `json:"-" yaml:"authentication,omitempty" mapstructure:"authentication"`
MultiClusterOptions *multicluster.Options `json:"multicluster,omitempty" yaml:"multicluster,omitempty" mapstructure:"multicluster"`
// Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere,
// we can add these options to kubesphere command lines
AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"`
@@ -204,4 +206,8 @@ func (conf *Config) stripEmptyOptions() {
conf.NotificationOptions = nil
}
if conf.MultiClusterOptions != nil && !conf.MultiClusterOptions.Enable {
conf.MultiClusterOptions = nil
}
}

View File

@@ -14,6 +14,7 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus"
"kubesphere.io/kubesphere/pkg/simple/client/multicluster"
"kubesphere.io/kubesphere/pkg/simple/client/network"
"kubesphere.io/kubesphere/pkg/simple/client/notification"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
@@ -118,6 +119,9 @@ func newTestConfig() (*Config, error) {
AccessTokenInactivityTimeout: 0,
},
},
MultiClusterOptions: &multicluster.Options{
Enable: false,
},
}
return conf, nil
}

View File

@@ -11,6 +11,7 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
"net/http"
"net/url"
"strings"
)
@@ -20,13 +21,11 @@ type Dispatcher interface {
}
type clusterDispatch struct {
agentLister v1alpha1.AgentLister
clusterLister v1alpha1.ClusterLister
}
func NewClusterDispatch(agentLister v1alpha1.AgentLister, clusterLister v1alpha1.ClusterLister) Dispatcher {
func NewClusterDispatch(clusterLister v1alpha1.ClusterLister) Dispatcher {
return &clusterDispatch{
agentLister: agentLister,
clusterLister: clusterLister,
}
}
@@ -58,23 +57,19 @@ func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, han
return
}
agent, err := c.agentLister.Get(info.Cluster)
if err != nil {
if errors.IsNotFound(err) {
http.Error(w, fmt.Sprintf("cluster %s not found", info.Cluster), http.StatusNotFound)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
if !isAgentReady(agent) {
if !isClusterReady(cluster) {
http.Error(w, fmt.Sprintf("cluster agent is not ready"), http.StatusInternalServerError)
return
}
endpoint, err := url.Parse(cluster.Spec.Connection.KubeSphereAPIEndpoint)
if err != nil {
klog.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
u := *req.URL
u.Host = agent.Spec.Proxy
u.Host = endpoint.Host
u.Path = strings.Replace(u.Path, fmt.Sprintf("/clusters/%s", info.Cluster), "", 1)
httpProxy := proxy.NewUpgradeAwareHandler(&u, http.DefaultTransport, true, false, c)
@@ -85,9 +80,9 @@ func (c *clusterDispatch) Error(w http.ResponseWriter, req *http.Request, err er
responsewriters.InternalError(w, req, err)
}
func isAgentReady(agent *clusterv1alpha1.Agent) bool {
for _, condition := range agent.Status.Conditions {
if condition.Type == clusterv1alpha1.AgentConnected && condition.Status == corev1.ConditionTrue {
func isClusterReady(cluster *clusterv1alpha1.Cluster) bool {
for _, condition := range cluster.Status.Conditions {
if condition.Type == clusterv1alpha1.ClusterReady && condition.Status == corev1.ConditionTrue {
return true
}
}
@@ -95,7 +90,6 @@ func isAgentReady(agent *clusterv1alpha1.Agent) bool {
return false
}
//
func isClusterHostCluster(cluster *clusterv1alpha1.Cluster) bool {
for key, value := range cluster.Annotations {
if key == clusterv1alpha1.IsHostCluster && value == "true" {

View File

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

View File

@@ -26,7 +26,6 @@ import (
type ClusterV1alpha1Interface interface {
RESTClient() rest.Interface
AgentsGetter
ClustersGetter
}
@@ -35,10 +34,6 @@ type ClusterV1alpha1Client struct {
restClient rest.Interface
}
func (c *ClusterV1alpha1Client) Agents() AgentInterface {
return newAgents(c)
}
func (c *ClusterV1alpha1Client) Clusters() ClusterInterface {
return newClusters(c)
}

View File

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

View File

@@ -28,10 +28,6 @@ type FakeClusterV1alpha1 struct {
*testing.Fake
}
func (c *FakeClusterV1alpha1) Agents() v1alpha1.AgentInterface {
return &FakeAgents{c}
}
func (c *FakeClusterV1alpha1) Clusters() v1alpha1.ClusterInterface {
return &FakeClusters{c}
}

View File

@@ -18,6 +18,4 @@ limitations under the License.
package v1alpha1
type AgentExpansion interface{}
type ClusterExpansion interface{}

View File

@@ -1,88 +0,0 @@
/*
Copyright 2019 The KubeSphere authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
time "time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
)
// AgentInformer provides access to a shared informer and lister for
// Agents.
type AgentInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.AgentLister
}
type agentInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// NewAgentInformer constructs a new informer for Agent 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 NewAgentInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredAgentInformer(client, resyncPeriod, indexers, nil)
}
// NewFilteredAgentInformer constructs a new informer for Agent 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 NewFilteredAgentInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ClusterV1alpha1().Agents().List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.ClusterV1alpha1().Agents().Watch(options)
},
},
&clusterv1alpha1.Agent{},
resyncPeriod,
indexers,
)
}
func (f *agentInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredAgentInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *agentInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&clusterv1alpha1.Agent{}, f.defaultInformer)
}
func (f *agentInformer) Lister() v1alpha1.AgentLister {
return v1alpha1.NewAgentLister(f.Informer().GetIndexer())
}

View File

@@ -24,8 +24,6 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// Agents returns a AgentInformer.
Agents() AgentInformer
// Clusters returns a ClusterInformer.
Clusters() ClusterInformer
}
@@ -41,11 +39,6 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// Agents returns a AgentInformer.
func (v *version) Agents() AgentInformer {
return &agentInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}
// Clusters returns a ClusterInformer.
func (v *version) Clusters() ClusterInformer {
return &clusterInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}

View File

@@ -59,8 +59,6 @@ func (f *genericInformer) Lister() cache.GenericLister {
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
switch resource {
// Group=cluster.kubesphere.io, Version=v1alpha1
case v1alpha1.SchemeGroupVersion.WithResource("agents"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Cluster().V1alpha1().Agents().Informer()}, nil
case v1alpha1.SchemeGroupVersion.WithResource("clusters"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Cluster().V1alpha1().Clusters().Informer()}, nil

View File

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

View File

@@ -18,10 +18,6 @@ limitations under the License.
package v1alpha1
// AgentListerExpansion allows custom methods to be added to
// AgentLister.
type AgentListerExpansion interface{}
// ClusterListerExpansion allows custom methods to be added to
// ClusterLister.
type ClusterListerExpansion interface{}

View File

@@ -3,23 +3,30 @@ package cluster
import (
"fmt"
v1 "k8s.io/api/core/v1"
apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/retry"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog"
clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1"
clusterclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1"
clusterinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster/v1alpha1"
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
"math/rand"
"reflect"
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
"time"
)
@@ -30,17 +37,31 @@ const (
//
// 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s
maxRetries = 15
kubefedNamespace = "kube-federation-system"
hostClusterName = "kubesphere"
// allocate kubernetesAPIServer port in range [portRangeMin, portRangeMax] for agents if port is not specified
// kubesphereAPIServer port is defaulted to kubernetesAPIServerPort + 10000
portRangeMin = 6000
portRangeMax = 7000
// Service port
kubernetesPort = 6443
kubespherePort = 80
defaultAgentNamespace = "kubesphere-system"
)
type ClusterController struct {
eventBroadcaster record.EventBroadcaster
eventRecorder record.EventRecorder
agentClient clusterclient.AgentInterface
clusterClient clusterclient.ClusterInterface
client kubernetes.Interface
hostConfig *rest.Config
agentLister clusterlister.AgentLister
agentHasSynced cache.InformerSynced
clusterClient clusterclient.ClusterInterface
clusterLister clusterlister.ClusterLister
clusterHasSynced cache.InformerSynced
@@ -52,9 +73,8 @@ type ClusterController struct {
func NewClusterController(
client kubernetes.Interface,
config *rest.Config,
clusterInformer clusterinformer.ClusterInformer,
agentInformer clusterinformer.AgentInformer,
agentClient clusterclient.AgentInterface,
clusterClient clusterclient.ClusterInterface,
) *ClusterController {
@@ -62,38 +82,35 @@ func NewClusterController(
broadcaster.StartLogging(func(format string, args ...interface{}) {
klog.Info(fmt.Sprintf(format, args))
})
broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")})
broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: client.CoreV1().Events("")})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cluster-controller"})
c := &ClusterController{
eventBroadcaster: broadcaster,
eventRecorder: recorder,
agentClient: agentClient,
client: client,
hostConfig: config,
clusterClient: clusterClient,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "cluster"),
workerLoopPeriod: time.Second,
}
c.agentLister = agentInformer.Lister()
c.agentHasSynced = agentInformer.Informer().HasSynced
c.clusterLister = clusterInformer.Lister()
c.clusterHasSynced = clusterInformer.Informer().HasSynced
clusterInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.addCluster,
UpdateFunc: func(oldObj, newObj interface{}) {
newCluster := newObj.(*clusterv1alpha1.Cluster)
oldCluster := oldObj.(*clusterv1alpha1.Cluster)
if newCluster.ResourceVersion == oldCluster.ResourceVersion {
return
}
c.addCluster(newObj)
},
DeleteFunc: c.addCluster,
})
agentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: nil,
UpdateFunc: nil,
DeleteFunc: nil,
})
return c
}
@@ -108,7 +125,7 @@ func (c *ClusterController) Run(workers int, stopCh <-chan struct{}) error {
klog.V(0).Info("starting cluster controller")
defer klog.Info("shutting down cluster controller")
if !cache.WaitForCacheSync(stopCh, c.clusterHasSynced, c.agentHasSynced) {
if !cache.WaitForCacheSync(stopCh, c.clusterHasSynced) {
return fmt.Errorf("failed to wait for caches to sync")
}
@@ -156,87 +173,211 @@ func (c *ClusterController) syncCluster(key string) error {
// cluster not found, possibly been deleted
// need to do the cleanup
if errors.IsNotFound(err) {
_, err = c.agentLister.Get(name)
if err != nil && errors.IsNotFound(err) {
return nil
}
if err != nil {
klog.Errorf("Failed to get cluster agent %s, %#v", name, err)
return err
}
// do the real cleanup work
err = c.agentClient.Delete(name, &metav1.DeleteOptions{})
return err
return nil
}
klog.Errorf("Failed to get cluster with name %s, %#v", name, err)
return err
}
newAgent := &clusterv1alpha1.Agent{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"app.kubernetes.io/name": "tower",
"cluster.kubesphere.io/name": name,
},
},
Spec: clusterv1alpha1.AgentSpec{
Token: "",
KubeSphereAPIServerPort: 0,
KubernetesAPIServerPort: 0,
Proxy: "",
Paused: !cluster.Spec.Active,
},
}
// proxy service name if needed
serviceName := fmt.Sprintf("mc-%s", cluster.Name)
agent, err := c.agentLister.Get(name)
if err != nil && errors.IsNotFound(err) {
agent, err = c.agentClient.Create(newAgent)
if err != nil {
klog.Errorf("Failed to create agent %s, %#v", name, err)
return err
if cluster.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !sets.NewString(cluster.ObjectMeta.Finalizers...).Has(clusterv1alpha1.Finalizer) {
cluster.ObjectMeta.Finalizers = append(cluster.ObjectMeta.Finalizers, clusterv1alpha1.Finalizer)
if cluster, err = c.clusterClient.Update(cluster); err != nil {
return err
}
}
} else {
// The object is being deleted
if sets.NewString(cluster.ObjectMeta.Finalizers...).Has(clusterv1alpha1.Finalizer) {
// need to unJoin federation first, before there are
// some cleanup work to do in member cluster which depends
// agent to proxy traffic
err = c.unJoinFederation(nil, name)
if err != nil {
klog.Errorf("Failed to unjoin federation for cluster %s, error %v", name, err)
return err
}
_, err = c.client.CoreV1().Services(defaultAgentNamespace).Get(serviceName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
// nothing to do
} else {
klog.Errorf("Failed to get proxy service %s, error %v", serviceName, err)
return err
}
} else {
err = c.client.CoreV1().Services(defaultAgentNamespace).Delete(serviceName, metav1.NewDeleteOptions(0))
if err != nil {
klog.Errorf("Unable to delete service %s, error %v", serviceName, err)
return err
}
}
finalizers := sets.NewString(cluster.ObjectMeta.Finalizers...)
finalizers.Delete(clusterv1alpha1.Finalizer)
cluster.ObjectMeta.Finalizers = finalizers.List()
if _, err = c.clusterClient.Update(cluster); err != nil {
return err
}
}
return nil
}
oldCluster := cluster.DeepCopy()
// prepare for proxy to member cluster
if cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeProxy {
if cluster.Spec.Connection.KubeSphereAPIServerPort == 0 ||
cluster.Spec.Connection.KubernetesAPIServerPort == 0 {
port, err := c.allocatePort()
if err != nil {
klog.Error(err)
return err
}
cluster.Spec.Connection.KubernetesAPIServerPort = port
cluster.Spec.Connection.KubeSphereAPIServerPort = port + 10000
}
// token uninitialized, generate a new token
if len(cluster.Spec.Connection.Token) == 0 {
cluster.Spec.Connection.Token = c.generateToken()
}
mcService := v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Namespace: cluster.Namespace,
Labels: map[string]string{
"app.kubernetes.io/name": serviceName,
"app": serviceName,
},
},
Spec: v1.ServiceSpec{
Selector: map[string]string{
"app.kubernetes.io/name": "tower",
"app": "tower",
},
Ports: []v1.ServicePort{
{
Name: "kubernetes",
Protocol: v1.ProtocolTCP,
Port: kubernetesPort,
TargetPort: intstr.FromInt(int(cluster.Spec.Connection.KubernetesAPIServerPort)),
},
{
Name: "kubesphere",
Protocol: v1.ProtocolTCP,
Port: kubespherePort,
TargetPort: intstr.FromInt(int(cluster.Spec.Connection.KubeSphereAPIServerPort)),
},
},
},
}
service, err := c.client.CoreV1().Services(defaultAgentNamespace).Get(serviceName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
service, err = c.client.CoreV1().Services(defaultAgentNamespace).Create(&mcService)
if err != nil {
return err
}
}
return err
} else {
if !reflect.DeepEqual(service.Spec, mcService.Spec) {
mcService.ObjectMeta = service.ObjectMeta
mcService.Spec.ClusterIP = service.Spec.ClusterIP
service, err = c.client.CoreV1().Services(defaultAgentNamespace).Update(&mcService)
if err != nil {
return err
}
}
}
// populated the kubernetes apiEndpoint and kubesphere apiEndpoint
cluster.Spec.Connection.KubernetesAPIEndpoint = fmt.Sprintf("https://%s:%d", service.Spec.ClusterIP, kubernetesPort)
cluster.Spec.Connection.KubeSphereAPIEndpoint = fmt.Sprintf("http://%s:%d", service.Spec.ClusterIP, kubespherePort)
if !reflect.DeepEqual(oldCluster.Spec, cluster.Spec) {
cluster, err = c.clusterClient.Update(cluster)
if err != nil {
klog.Errorf("Error updating cluster %s, error %s", cluster.Name, err)
return err
}
return nil
}
}
if len(cluster.Spec.Connection.KubeConfig) == 0 {
return nil
}
var clientSet kubernetes.Interface
var clusterConfig *rest.Config
// prepare for
clientConfig, err := clientcmd.NewClientConfigFromBytes(cluster.Spec.Connection.KubeConfig)
if err != nil {
klog.Errorf("Failed to get agent %s, %#v", name, err)
klog.Errorf("Unable to create client config from kubeconfig bytes, %#v", err)
return err
}
if agent.Spec.Paused != newAgent.Spec.Paused {
agent.Spec.Paused = newAgent.Spec.Paused
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
_, err = c.agentClient.Update(agent)
return err
})
clusterConfig, err = clientConfig.ClientConfig()
if err != nil {
klog.Errorf("Failed to get client config, %#v", err)
return err
}
// agent connection is ready, update cluster status
// set
if len(agent.Status.KubeConfig) != 0 && c.isAgentReady(agent) {
clientConfig, err := clientcmd.NewClientConfigFromBytes(agent.Status.KubeConfig)
clientSet, err = kubernetes.NewForConfig(clusterConfig)
if err != nil {
klog.Errorf("Failed to create ClientSet from config, %#v", err)
return nil
}
if !cluster.Spec.JoinFederation { // trying to unJoin federation
err = c.unJoinFederation(clusterConfig, cluster.Name)
if err != nil {
klog.Errorf("Unable to create client config from kubeconfig bytes, %#v", err)
klog.Errorf("Failed to unJoin federation for cluster %s, error %v", cluster.Name, err)
c.eventRecorder.Event(cluster, v1.EventTypeWarning, "UnJoinFederation", err.Error())
return err
}
config, err := clientConfig.ClientConfig()
} else { // join federation
_, err = c.joinFederation(clusterConfig, cluster.Name, cluster.Labels)
if err != nil {
klog.Errorf("Failed to get client config, %#v", err)
klog.Errorf("Failed to join federation for cluster %s, error %v", cluster.Name, err)
c.eventRecorder.Event(cluster, v1.EventTypeWarning, "JoinFederation", err.Error())
return err
}
c.eventRecorder.Event(cluster, v1.EventTypeNormal, "JoinFederation", "Cluster has joined federation.")
clientSet, err := kubernetes.NewForConfig(config)
if err != nil {
klog.Errorf("Failed to create ClientSet from config, %#v", err)
return nil
federationReadyCondition := clusterv1alpha1.ClusterCondition{
Type: clusterv1alpha1.ClusterFederated,
Status: v1.ConditionTrue,
LastUpdateTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "",
Message: "Cluster has joined federation control plane successfully",
}
c.updateClusterCondition(cluster, federationReadyCondition)
}
// cluster agent is ready, we can pull kubernetes cluster info through agent
// since there is no agent necessary for host cluster, so updates for host cluster
// is safe.
if isConditionTrue(cluster, clusterv1alpha1.ClusterAgentAvailable) ||
cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeDirect {
version, err := clientSet.Discovery().ServerVersion()
if err != nil {
klog.Errorf("Failed to get kubernetes version, %#v", err)
@@ -252,28 +393,25 @@ func (c *ClusterController) syncCluster(key string) error {
}
cluster.Status.NodeCount = len(nodes.Items)
clusterReadyCondition := clusterv1alpha1.ClusterCondition{
Type: clusterv1alpha1.ClusterReady,
Status: v1.ConditionTrue,
LastUpdateTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: string(clusterv1alpha1.ClusterReady),
Message: "Cluster is available now",
}
c.updateClusterCondition(cluster, clusterReadyCondition)
}
agentReadyCondition := clusterv1alpha1.ClusterCondition{
Type: clusterv1alpha1.ClusterAgentAvailable,
LastUpdateTime: metav1.NewTime(time.Now()),
LastTransitionTime: metav1.NewTime(time.Now()),
Reason: "",
Message: "Cluster agent is available now.",
}
if c.isAgentReady(agent) {
agentReadyCondition.Status = v1.ConditionTrue
} else {
agentReadyCondition.Status = v1.ConditionFalse
}
c.addClusterCondition(cluster, agentReadyCondition)
_, err = c.clusterClient.Update(cluster)
if err != nil {
klog.Errorf("Failed to update cluster status, %#v", err)
return err
if !reflect.DeepEqual(oldCluster, cluster) {
_, err = c.clusterClient.Update(cluster)
if err != nil {
klog.Errorf("Failed to update cluster status, %#v", err)
return err
}
}
return nil
@@ -298,50 +436,126 @@ func (c *ClusterController) handleErr(err error, key interface{}) {
}
if c.queue.NumRequeues(key) < maxRetries {
klog.V(2).Infof("Error syncing virtualservice %s for service retrying, %#v", key, err)
klog.V(2).Infof("Error syncing cluster %s, retrying, %v", key, err)
c.queue.AddRateLimited(key)
return
}
klog.V(4).Infof("Dropping service %s out of the queue.", key)
klog.V(4).Infof("Dropping cluster %s out of the queue.", key)
c.queue.Forget(key)
utilruntime.HandleError(err)
}
func (c *ClusterController) addAgent(obj interface{}) {
agent := obj.(*clusterv1alpha1.Agent)
key, err := cache.MetaNamespaceKeyFunc(obj)
if err != nil {
utilruntime.HandleError(fmt.Errorf("get agent key %s failed", agent.Name))
return
}
c.queue.Add(key)
}
func (c *ClusterController) isAgentReady(agent *clusterv1alpha1.Agent) bool {
for _, condition := range agent.Status.Conditions {
if condition.Type == clusterv1alpha1.AgentConnected && condition.Status == v1.ConditionTrue {
func isConditionTrue(cluster *clusterv1alpha1.Cluster, conditionType clusterv1alpha1.ClusterConditionType) bool {
for _, condition := range cluster.Status.Conditions {
if condition.Type == conditionType && condition.Status == v1.ConditionTrue {
return true
}
}
return false
}
// addClusterCondition add condition
func (c *ClusterController) addClusterCondition(cluster *clusterv1alpha1.Cluster, condition clusterv1alpha1.ClusterCondition) {
// updateClusterCondition updates condition in cluster conditions using giving condition
// adds condition if not existed
func (c *ClusterController) updateClusterCondition(cluster *clusterv1alpha1.Cluster, condition clusterv1alpha1.ClusterCondition) {
if cluster.Status.Conditions == nil {
cluster.Status.Conditions = make([]clusterv1alpha1.ClusterCondition, 0)
}
newConditions := make([]clusterv1alpha1.ClusterCondition, 0)
needToUpdate := true
for _, cond := range cluster.Status.Conditions {
if cond.Type == condition.Type {
continue
if cond.Status == condition.Status {
needToUpdate = false
continue
} else {
newConditions = append(newConditions, cond)
}
}
newConditions = append(newConditions, cond)
}
newConditions = append(newConditions, condition)
cluster.Status.Conditions = newConditions
if needToUpdate {
newConditions = append(newConditions, condition)
cluster.Status.Conditions = newConditions
}
}
func isHostCluster(cluster *clusterv1alpha1.Cluster) bool {
for k, v := range cluster.Annotations {
if k == clusterv1alpha1.IsHostCluster && v == "true" {
return true
}
}
return false
}
// joinFederation joins a cluster into federation clusters.
// return nil error if kubefed cluster already exists.
func (c *ClusterController) joinFederation(clusterConfig *rest.Config, joiningClusterName string, labels map[string]string) (*fedv1b1.KubeFedCluster, error) {
return joinClusterForNamespace(c.hostConfig,
clusterConfig,
kubefedNamespace,
kubefedNamespace,
hostClusterName,
joiningClusterName,
fmt.Sprintf("%s-secret", joiningClusterName),
labels,
apiextv1b1.ClusterScoped,
false,
false)
}
// unJoinFederation unjoins a cluster from federation control plane.
func (c *ClusterController) unJoinFederation(clusterConfig *rest.Config, unjoiningClusterName string) error {
return unjoinCluster(c.hostConfig,
clusterConfig,
kubefedNamespace,
hostClusterName,
unjoiningClusterName,
true,
false)
}
// allocatePort find a available port between [portRangeMin, portRangeMax] in maximumRetries
// TODO: only works with handful clusters
func (c *ClusterController) allocatePort() (uint16, error) {
rand.Seed(time.Now().UnixNano())
clusters, err := c.clusterLister.List(labels.Everything())
if err != nil {
return 0, err
}
const maximumRetries = 10
for i := 0; i < maximumRetries; i++ {
collision := false
port := uint16(portRangeMin + rand.Intn(portRangeMax-portRangeMin+1))
for _, item := range clusters {
if item.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeProxy &&
item.Spec.Connection.KubernetesAPIServerPort != 0 &&
item.Spec.Connection.KubeSphereAPIServerPort == port {
collision = true
break
}
}
if !collision {
return port, nil
}
}
return 0, fmt.Errorf("unable to allocate port after %d retries", maximumRetries)
}
// generateToken returns a random 32-byte string as token
func (c *ClusterController) generateToken() string {
rand.Seed(time.Now().UnixNano())
b := make([]byte, 32)
rand.Read(b)
return fmt.Sprintf("%x", b)
}

View File

@@ -0,0 +1 @@
package cluster

View File

@@ -0,0 +1,720 @@
package cluster
import (
"context"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
kubeclient "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog"
"reflect"
"sigs.k8s.io/kubefed/pkg/kubefedctl/util"
"time"
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
genericclient "sigs.k8s.io/kubefed/pkg/client/generic"
)
var (
// Policy rules allowing full access to resources in the cluster
// or namespace.
namespacedPolicyRules = []rbacv1.PolicyRule{
{
Verbs: []string{rbacv1.VerbAll},
APIGroups: []string{rbacv1.APIGroupAll},
Resources: []string{rbacv1.ResourceAll},
},
}
clusterPolicyRules = []rbacv1.PolicyRule{
namespacedPolicyRules[0],
{
NonResourceURLs: []string{rbacv1.NonResourceAll},
Verbs: []string{"get"},
},
}
)
const (
tokenKey = "token"
serviceAccountSecretTimeout = 30 * time.Second
)
// joinClusterForNamespace registers a cluster with a KubeFed control
// plane. The KubeFed namespace in the joining cluster is provided by
// the joiningNamespace parameter.
func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedNamespace,
joiningNamespace, hostClusterName, joiningClusterName, secretName string, labels map[string]string,
scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) {
hostClientset, err := HostClientset(hostConfig)
if err != nil {
klog.V(2).Infof("Failed to get host cluster clientset: %v", err)
return nil, err
}
clusterClientset, err := ClusterClientset(clusterConfig)
if err != nil {
klog.V(2).Infof("Failed to get joining cluster clientset: %v", err)
return nil, err
}
client, err := genericclient.New(hostConfig)
if err != nil {
klog.V(2).Infof("Failed to get kubefed clientset: %v", err)
return nil, err
}
klog.V(2).Infof("Performing preflight checks.")
err = performPreflightChecks(clusterClientset, joiningClusterName, hostClusterName, joiningNamespace, errorOnExisting)
if err != nil {
return nil, err
}
klog.V(2).Infof("Creating %s namespace in joining cluster", joiningNamespace)
_, err = createKubeFedNamespace(clusterClientset, joiningNamespace, joiningClusterName, dryRun)
if err != nil {
klog.V(2).Infof("Error creating %s namespace in joining cluster: %v", joiningNamespace, err)
return nil, err
}
klog.V(2).Infof("Created %s namespace in joining cluster", joiningNamespace)
saName, err := createAuthorizedServiceAccount(clusterClientset, joiningNamespace, joiningClusterName, hostClusterName, scope, dryRun, errorOnExisting)
if err != nil {
return nil, err
}
secret, _, err := populateSecretInHostCluster(clusterClientset, hostClientset,
saName, kubefedNamespace, joiningNamespace, joiningClusterName, secretName, dryRun)
if err != nil {
klog.V(2).Infof("Error creating secret in host cluster: %s due to: %v", hostClusterName, err)
return nil, err
}
var disabledTLSValidations []fedv1b1.TLSValidation
if clusterConfig.TLSClientConfig.Insecure {
disabledTLSValidations = append(disabledTLSValidations, fedv1b1.TLSAll)
}
kubefedCluster, err := createKubeFedCluster(client, joiningClusterName, clusterConfig.Host,
secret.Name, kubefedNamespace, clusterConfig.CAData, disabledTLSValidations, labels, dryRun, errorOnExisting)
if err != nil {
klog.V(2).Infof("Failed to create federated cluster resource: %v", err)
return nil, err
}
klog.V(2).Info("Created federated cluster resource")
return kubefedCluster, nil
}
// performPreflightChecks checks that the host and joining clusters are in
// a consistent state.
func performPreflightChecks(clusterClientset kubeclient.Interface, name, hostClusterName,
kubefedNamespace string, errorOnExisting bool) error {
// Make sure there is no existing service account in the joining cluster.
saName := util.ClusterServiceAccountName(name, hostClusterName)
_, err := clusterClientset.CoreV1().ServiceAccounts(kubefedNamespace).Get(saName, metav1.GetOptions{})
switch {
case apierrors.IsNotFound(err):
return nil
case err != nil:
return err
case errorOnExisting:
return errors.Errorf("service account: %s already exists in joining cluster: %s", saName, name)
default:
klog.V(2).Infof("Service account %s already exists in joining cluster %s", saName, name)
return nil
}
}
// createKubeFedCluster creates a federated cluster resource that associates
// the cluster and secret.
func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEndpoint,
secretName, kubefedNamespace string, caBundle []byte, disabledTLSValidations []fedv1b1.TLSValidation,
labels map[string]string, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) {
fedCluster := &fedv1b1.KubeFedCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: kubefedNamespace,
Name: joiningClusterName,
Labels: labels,
},
Spec: fedv1b1.KubeFedClusterSpec{
APIEndpoint: apiEndpoint,
CABundle: caBundle,
SecretRef: fedv1b1.LocalSecretReference{
Name: secretName,
},
DisabledTLSValidations: disabledTLSValidations,
},
}
if dryRun {
return fedCluster, nil
}
existingFedCluster := &fedv1b1.KubeFedCluster{}
err := client.Get(context.TODO(), existingFedCluster, kubefedNamespace, joiningClusterName)
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not retrieve federated cluster %s due to %v", joiningClusterName, err)
return nil, err
case err == nil && errorOnExisting:
return nil, errors.Errorf("federated cluster %s already exists in host cluster", joiningClusterName)
case err == nil:
existingFedCluster.Spec = fedCluster.Spec
existingFedCluster.Labels = labels
err = client.Update(context.TODO(), existingFedCluster)
if err != nil {
klog.V(2).Infof("Could not update federated cluster %s due to %v", fedCluster.Name, err)
return nil, err
}
return existingFedCluster, nil
default:
err = client.Create(context.TODO(), fedCluster)
if err != nil {
klog.V(2).Infof("Could not create federated cluster %s due to %v", fedCluster.Name, err)
return nil, err
}
return fedCluster, nil
}
}
// createKubeFedNamespace creates the kubefed namespace in the cluster
// associated with clusterClientset, if it doesn't already exist.
func createKubeFedNamespace(clusterClientset kubeclient.Interface, kubefedNamespace,
joiningClusterName string, dryRun bool) (*corev1.Namespace, error) {
fedNamespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: kubefedNamespace,
},
}
if dryRun {
return fedNamespace, nil
}
_, err := clusterClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
klog.V(2).Infof("Could not get %s namespace: %v", kubefedNamespace, err)
return nil, err
}
if err == nil {
klog.V(2).Infof("Already existing %s namespace", kubefedNamespace)
return fedNamespace, nil
}
// Not found, so create.
_, err = clusterClientset.CoreV1().Namespaces().Create(fedNamespace)
if err != nil && !apierrors.IsAlreadyExists(err) {
klog.V(2).Infof("Could not create %s namespace: %v", kubefedNamespace, err)
return nil, err
}
return fedNamespace, nil
}
// createAuthorizedServiceAccount creates a service account and grants
// the privileges required by the KubeFed control plane to manage
// resources in the joining cluster. The name of the created service
// account is returned on success.
func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface,
namespace, joiningClusterName, hostClusterName string,
scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (string, error) {
klog.V(2).Infof("Creating service account in joining cluster: %s", joiningClusterName)
saName, err := createServiceAccount(joiningClusterClientset, namespace,
joiningClusterName, hostClusterName, dryRun, errorOnExisting)
if err != nil {
klog.V(2).Infof("Error creating service account: %s in joining cluster: %s due to: %v",
saName, joiningClusterName, err)
return "", err
}
klog.V(2).Infof("Created service account: %s in joining cluster: %s", saName, joiningClusterName)
if scope == apiextv1b1.NamespaceScoped {
klog.V(2).Infof("Creating role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName)
err = createRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, dryRun, errorOnExisting)
if err != nil {
klog.V(2).Infof("Error creating role and binding for service account: %s in joining cluster: %s due to: %v", saName, joiningClusterName, err)
return "", err
}
klog.V(2).Infof("Created role and binding for service account: %s in joining cluster: %s",
saName, joiningClusterName)
klog.V(2).Infof("Creating health check cluster role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName)
err = createHealthCheckClusterRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName,
dryRun, errorOnExisting)
if err != nil {
klog.V(2).Infof("Error creating health check cluster role and binding for service account: %s in joining cluster: %s due to: %v",
saName, joiningClusterName, err)
return "", err
}
klog.V(2).Infof("Created health check cluster role and binding for service account: %s in joining cluster: %s",
saName, joiningClusterName)
} else {
klog.V(2).Infof("Creating cluster role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName)
err = createClusterRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, dryRun, errorOnExisting)
if err != nil {
klog.V(2).Infof("Error creating cluster role and binding for service account: %s in joining cluster: %s due to: %v",
saName, joiningClusterName, err)
return "", err
}
klog.V(2).Infof("Created cluster role and binding for service account: %s in joining cluster: %s",
saName, joiningClusterName)
}
return saName, nil
}
// createServiceAccount creates a service account in the cluster associated
// with clusterClientset with credentials that will be used by the host cluster
// to access its API server.
func createServiceAccount(clusterClientset kubeclient.Interface, namespace,
joiningClusterName, hostClusterName string, dryRun, errorOnExisting bool) (string, error) {
saName := util.ClusterServiceAccountName(joiningClusterName, hostClusterName)
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: saName,
Namespace: namespace,
},
}
if dryRun {
return saName, nil
}
// Create a new service account.
_, err := clusterClientset.CoreV1().ServiceAccounts(namespace).Create(sa)
switch {
case apierrors.IsAlreadyExists(err) && errorOnExisting:
klog.V(2).Infof("Service account %s/%s already exists in target cluster %s", namespace, saName, joiningClusterName)
return "", err
case err != nil && !apierrors.IsAlreadyExists(err):
klog.V(2).Infof("Could not create service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err)
return "", err
default:
return saName, nil
}
}
func bindingSubjects(saName, namespace string) []rbacv1.Subject {
return []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Name: saName,
Namespace: namespace,
},
}
}
// createClusterRoleAndBinding creates an RBAC cluster role and
// binding that allows the service account identified by saName to
// access all resources in all namespaces in the cluster associated
// with clientset.
func createClusterRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error {
if dryRun {
return nil
}
roleName := util.RoleName(saName)
role := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
},
Rules: clusterPolicyRules,
}
existingRole, err := clientset.RbacV1().ClusterRoles().Get(roleName, metav1.GetOptions{})
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not get cluster role for service account %s in joining cluster %s due to %v",
saName, clusterName, err)
return err
case err == nil && errorOnExisting:
return errors.Errorf("cluster role for service account %s in joining cluster %s already exists", saName, clusterName)
case err == nil:
existingRole.Rules = role.Rules
_, err := clientset.RbacV1().ClusterRoles().Update(existingRole)
if err != nil {
klog.V(2).Infof("Could not update cluster role for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
default: // role was not found
_, err := clientset.RbacV1().ClusterRoles().Create(role)
if err != nil {
klog.V(2).Infof("Could not create cluster role for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
// TODO: This should limit its access to only necessary resources.
binding := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
},
Subjects: bindingSubjects(saName, namespace),
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "ClusterRole",
Name: roleName,
},
}
existingBinding, err := clientset.RbacV1().ClusterRoleBindings().Get(binding.Name, metav1.GetOptions{})
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not get cluster role binding for service account %s in joining cluster %s due to %v",
saName, clusterName, err)
return err
case err == nil && errorOnExisting:
return errors.Errorf("cluster role binding for service account %s in joining cluster %s already exists", saName, clusterName)
case err == nil:
// The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding
// must be deleted and recreated with the correct roleRef
if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) {
err = clientset.RbacV1().ClusterRoleBindings().Delete(existingBinding.Name, &metav1.DeleteOptions{})
if err != nil {
klog.V(2).Infof("Could not delete existing cluster role binding for service account %s in joining cluster %s due to: %v",
saName, clusterName, err)
return err
}
_, err = clientset.RbacV1().ClusterRoleBindings().Create(binding)
if err != nil {
klog.V(2).Infof("Could not create cluster role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
} else {
existingBinding.Subjects = binding.Subjects
_, err := clientset.RbacV1().ClusterRoleBindings().Update(existingBinding)
if err != nil {
klog.V(2).Infof("Could not update cluster role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
default:
_, err = clientset.RbacV1().ClusterRoleBindings().Create(binding)
if err != nil {
klog.V(2).Infof("Could not create cluster role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
return nil
}
// createRoleAndBinding creates an RBAC role and binding
// that allows the service account identified by saName to access all
// resources in the specified namespace.
func createRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error {
if dryRun {
return nil
}
roleName := util.RoleName(saName)
role := &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
},
Rules: namespacedPolicyRules,
}
existingRole, err := clientset.RbacV1().Roles(namespace).Get(roleName, metav1.GetOptions{})
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not retrieve role for service account %s in joining cluster %s due to %v", saName, clusterName, err)
return err
case errorOnExisting && err == nil:
return errors.Errorf("role for service account %s in joining cluster %s already exists", saName, clusterName)
case err == nil:
existingRole.Rules = role.Rules
_, err = clientset.RbacV1().Roles(namespace).Update(existingRole)
if err != nil {
klog.V(2).Infof("Could not update role for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
default:
_, err := clientset.RbacV1().Roles(namespace).Create(role)
if err != nil {
klog.V(2).Infof("Could not create role for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
binding := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
},
Subjects: bindingSubjects(saName, namespace),
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "Role",
Name: roleName,
},
}
existingBinding, err := clientset.RbacV1().RoleBindings(namespace).Get(binding.Name, metav1.GetOptions{})
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not retrieve role binding for service account %s in joining cluster %s due to: %v",
saName, clusterName, err)
return err
case err == nil && errorOnExisting:
return errors.Errorf("role binding for service account %s in joining cluster %s already exists", saName, clusterName)
case err == nil:
// The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding
// must be deleted and recreated with the correct roleRef
if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) {
err = clientset.RbacV1().RoleBindings(namespace).Delete(existingBinding.Name, &metav1.DeleteOptions{})
if err != nil {
klog.V(2).Infof("Could not delete existing role binding for service account %s in joining cluster %s due to: %v",
saName, clusterName, err)
return err
}
_, err = clientset.RbacV1().RoleBindings(namespace).Create(binding)
if err != nil {
klog.V(2).Infof("Could not create role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
} else {
existingBinding.Subjects = binding.Subjects
_, err = clientset.RbacV1().RoleBindings(namespace).Update(existingBinding)
if err != nil {
klog.V(2).Infof("Could not update role binding for service account %s in joining cluster %s due to: %v",
saName, clusterName, err)
return err
}
}
default:
_, err = clientset.RbacV1().RoleBindings(namespace).Create(binding)
if err != nil {
klog.V(2).Infof("Could not create role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
return nil
}
// createHealthCheckClusterRoleAndBinding creates an RBAC cluster role and
// binding that allows the service account identified by saName to
// access the health check path of the cluster.
func createHealthCheckClusterRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error {
if dryRun {
return nil
}
roleName := util.HealthCheckRoleName(saName, namespace)
role := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
},
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"Get"},
NonResourceURLs: []string{"/healthz"},
},
// The cluster client expects to be able to list nodes to retrieve zone and region details.
// TODO(marun) Consider making zone/region retrieval optional
{
Verbs: []string{"list"},
APIGroups: []string{""},
Resources: []string{"nodes"},
},
},
}
existingRole, err := clientset.RbacV1().ClusterRoles().Get(role.Name, metav1.GetOptions{})
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not get health check cluster role for service account %s in joining cluster %s due to %v",
saName, clusterName, err)
return err
case err == nil && errorOnExisting:
return errors.Errorf("health check cluster role for service account %s in joining cluster %s already exists", saName, clusterName)
case err == nil:
existingRole.Rules = role.Rules
_, err := clientset.RbacV1().ClusterRoles().Update(existingRole)
if err != nil {
klog.V(2).Infof("Could not update health check cluster role for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
default: // role was not found
_, err := clientset.RbacV1().ClusterRoles().Create(role)
if err != nil {
klog.V(2).Infof("Could not create health check cluster role for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
binding := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
},
Subjects: bindingSubjects(saName, namespace),
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: "ClusterRole",
Name: roleName,
},
}
existingBinding, err := clientset.RbacV1().ClusterRoleBindings().Get(binding.Name, metav1.GetOptions{})
switch {
case err != nil && !apierrors.IsNotFound(err):
klog.V(2).Infof("Could not get health check cluster role binding for service account %s in joining cluster %s due to %v",
saName, clusterName, err)
return err
case err == nil && errorOnExisting:
return errors.Errorf("health check cluster role binding for service account %s in joining cluster %s already exists", saName, clusterName)
case err == nil:
// The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding
// must be deleted and recreated with the correct roleRef
if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) {
err = clientset.RbacV1().ClusterRoleBindings().Delete(existingBinding.Name, &metav1.DeleteOptions{})
if err != nil {
klog.V(2).Infof("Could not delete existing health check cluster role binding for service account %s in joining cluster %s due to: %v",
saName, clusterName, err)
return err
}
_, err = clientset.RbacV1().ClusterRoleBindings().Create(binding)
if err != nil {
klog.V(2).Infof("Could not create health check cluster role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
} else {
existingBinding.Subjects = binding.Subjects
_, err := clientset.RbacV1().ClusterRoleBindings().Update(existingBinding)
if err != nil {
klog.V(2).Infof("Could not update health check cluster role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
default:
_, err = clientset.RbacV1().ClusterRoleBindings().Create(binding)
if err != nil {
klog.V(2).Infof("Could not create health check cluster role binding for service account: %s in joining cluster: %s due to: %v",
saName, clusterName, err)
return err
}
}
return nil
}
// populateSecretInHostCluster copies the service account secret for saName
// from the cluster referenced by clusterClientset to the client referenced by
// hostClientset, putting it in a secret named secretName in the provided
// namespace.
func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Interface,
saName, hostNamespace, joiningNamespace, joiningClusterName, secretName string,
dryRun bool) (*corev1.Secret, []byte, error) {
klog.V(2).Infof("Creating cluster credentials secret in host cluster")
if dryRun {
dryRunSecret := &corev1.Secret{}
dryRunSecret.Name = secretName
return dryRunSecret, nil, nil
}
// Get the secret from the joining cluster.
var secret *corev1.Secret
err := wait.PollImmediate(1*time.Second, serviceAccountSecretTimeout, func() (bool, error) {
sa, err := clusterClientset.CoreV1().ServiceAccounts(joiningNamespace).Get(saName,
metav1.GetOptions{})
if err != nil {
return false, nil
}
for _, objReference := range sa.Secrets {
saSecretName := objReference.Name
var err error
secret, err = clusterClientset.CoreV1().Secrets(joiningNamespace).Get(saSecretName, metav1.GetOptions{})
if err != nil {
return false, nil
}
if secret.Type == corev1.SecretTypeServiceAccountToken {
klog.V(2).Infof("Using secret named: %s", secret.Name)
return true, nil
}
}
return false, nil
})
if err != nil {
klog.V(2).Infof("Could not get service account secret from joining cluster: %v", err)
return nil, nil, err
}
token, ok := secret.Data[tokenKey]
if !ok {
return nil, nil, errors.Errorf("Key %q not found in service account secret", tokenKey)
}
// Create a secret in the host cluster containing the token.
v1Secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: hostNamespace,
},
Data: map[string][]byte{
tokenKey: token,
},
}
if secretName == "" {
v1Secret.GenerateName = joiningClusterName + "-"
} else {
v1Secret.Name = secretName
}
var v1SecretResult *corev1.Secret
_, err = hostClientset.CoreV1().Secrets(hostNamespace).Get(v1Secret.Name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
v1SecretResult, err = hostClientset.CoreV1().Secrets(hostNamespace).Create(&v1Secret)
if err != nil {
klog.V(2).Infof("Could not create secret in host cluster: %v", err)
return nil, nil, err
}
return v1SecretResult, nil, nil
}
klog.V(2).Infof("Could not get secret %s in host cluster: %v", v1Secret.Name, err)
return nil, nil, err
} else {
v1SecretResult, err = hostClientset.CoreV1().Secrets(hostNamespace).Update(&v1Secret)
if err != nil {
klog.V(2).Infof("Update secret %s in host cluster failed: %v", v1Secret.Name, err)
return nil, nil, err
}
}
// caBundle is optional so no error is suggested if it is not
// found in the secret.
caBundle := secret.Data["ca.crt"]
klog.V(2).Infof("Created secret in host cluster named: %s", v1SecretResult.Name)
return v1SecretResult, caBundle, nil
}

View File

@@ -0,0 +1,296 @@
package cluster
import (
"context"
"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeclient "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog"
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
genericclient "sigs.k8s.io/kubefed/pkg/client/generic"
"sigs.k8s.io/kubefed/pkg/kubefedctl/util"
)
// Following code copied from sigs.k8s.io/kubefed to avoid import collision
// UnjoinCluster performs all the necessary steps to remove the
// registration of a cluster from a KubeFed control plane provided the
// required set of parameters are passed in.
func unjoinCluster(hostConfig, clusterConfig *rest.Config, kubefedNamespace, hostClusterName, unjoiningClusterName string, forceDeletion, dryRun bool) error {
hostClientset, err := util.HostClientset(hostConfig)
if err != nil {
klog.V(2).Infof("Failed to get host cluster clientset: %v", err)
return err
}
var clusterClientset *kubeclient.Clientset
if clusterConfig != nil {
clusterClientset, err = util.ClusterClientset(clusterConfig)
if err != nil {
klog.V(2).Infof("Failed to get unjoining cluster clientset: %v", err)
if !forceDeletion {
return err
}
}
}
client, err := genericclient.New(hostConfig)
if err != nil {
klog.V(2).Infof("Failed to get kubefed clientset: %v", err)
return err
}
if clusterClientset != nil {
err := deleteRBACResources(clusterClientset, kubefedNamespace, unjoiningClusterName, hostClusterName, forceDeletion, dryRun)
if err != nil {
if !forceDeletion {
return err
}
klog.V(2).Infof("Failed to delete RBAC resources: %v", err)
}
err = deleteFedNSFromUnjoinCluster(hostClientset, clusterClientset, kubefedNamespace, unjoiningClusterName, dryRun)
if err != nil {
if !forceDeletion {
return err
}
klog.V(2).Infof("Failed to delete kubefed namespace: %v", err)
}
}
// deletionSucceeded when all operations in deleteRBACResources and deleteFedNSFromUnjoinCluster succeed.
err = deleteFederatedClusterAndSecret(hostClientset, client, kubefedNamespace, unjoiningClusterName, forceDeletion, dryRun)
if err != nil {
return err
}
return nil
}
// deleteKubeFedClusterAndSecret deletes a federated cluster resource that associates
// the cluster and secret.
func deleteFederatedClusterAndSecret(hostClientset kubeclient.Interface, client genericclient.Client,
kubefedNamespace, unjoiningClusterName string, forceDeletion, dryRun bool) error {
if dryRun {
return nil
}
klog.V(2).Infof("Deleting kubefed cluster resource from namespace %q for unjoin cluster %q",
kubefedNamespace, unjoiningClusterName)
fedCluster := &fedv1b1.KubeFedCluster{}
err := client.Get(context.TODO(), fedCluster, kubefedNamespace, unjoiningClusterName)
if err != nil {
if apierrors.IsNotFound(err) {
return nil
}
return errors.Wrapf(err, "Failed to get kubefed cluster \"%s/%s\"", kubefedNamespace, unjoiningClusterName)
}
err = hostClientset.CoreV1().Secrets(kubefedNamespace).Delete(fedCluster.Spec.SecretRef.Name,
&metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("Secret \"%s/%s\" does not exist in the host cluster.", kubefedNamespace, fedCluster.Spec.SecretRef.Name)
} else if err != nil {
wrappedErr := errors.Wrapf(err, "Failed to delete secret \"%s/%s\" for unjoin cluster %q",
kubefedNamespace, fedCluster.Spec.SecretRef.Name, unjoiningClusterName)
if !forceDeletion {
return wrappedErr
}
klog.V(2).Infof("%v", wrappedErr)
} else {
klog.V(2).Infof("Deleted secret \"%s/%s\" for unjoin cluster %q", kubefedNamespace, fedCluster.Spec.SecretRef.Name, unjoiningClusterName)
}
err = client.Delete(context.TODO(), fedCluster, fedCluster.Namespace, fedCluster.Name)
if apierrors.IsNotFound(err) {
klog.V(2).Infof("KubeFed cluster \"%s/%s\" does not exist in the host cluster.", fedCluster.Namespace, fedCluster.Name)
} else if err != nil {
wrappedErr := errors.Wrapf(err, "Failed to delete kubefed cluster \"%s/%s\" for unjoin cluster %q", fedCluster.Namespace, fedCluster.Name, unjoiningClusterName)
if !forceDeletion {
return wrappedErr
}
klog.V(2).Infof("%v", wrappedErr)
} else {
klog.V(2).Infof("Deleted kubefed cluster \"%s/%s\" for unjoin cluster %q.", fedCluster.Namespace, fedCluster.Name, unjoiningClusterName)
}
return nil
}
// deleteRBACResources deletes the cluster role, cluster rolebindings and service account
// from the unjoining cluster.
func deleteRBACResources(unjoiningClusterClientset kubeclient.Interface,
namespace, unjoiningClusterName, hostClusterName string, forceDeletion, dryRun bool) error {
saName := ClusterServiceAccountName(unjoiningClusterName, hostClusterName)
err := deleteClusterRoleAndBinding(unjoiningClusterClientset, saName, namespace, unjoiningClusterName, forceDeletion, dryRun)
if err != nil {
return err
}
err = deleteServiceAccount(unjoiningClusterClientset, saName, namespace, unjoiningClusterName, dryRun)
if err != nil {
return err
}
return nil
}
// deleteFedNSFromUnjoinCluster deletes the kubefed namespace from
// the unjoining cluster so long as the unjoining cluster is not the
// host cluster.
func deleteFedNSFromUnjoinCluster(hostClientset, unjoiningClusterClientset kubeclient.Interface,
kubefedNamespace, unjoiningClusterName string, dryRun bool) error {
if dryRun {
return nil
}
hostClusterNamespace, err := hostClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{})
if err != nil {
return errors.Wrapf(err, "Error retrieving namespace %q from host cluster", kubefedNamespace)
}
unjoiningClusterNamespace, err := unjoiningClusterClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{})
if err != nil {
return errors.Wrapf(err, "Error retrieving namespace %q from unjoining cluster %q", kubefedNamespace, unjoiningClusterName)
}
if IsPrimaryCluster(hostClusterNamespace, unjoiningClusterNamespace) {
klog.V(2).Infof("The kubefed namespace %q does not need to be deleted from the host cluster by unjoin.", kubefedNamespace)
return nil
}
klog.V(2).Infof("Deleting kubefed namespace %q from unjoining cluster %q.", kubefedNamespace, unjoiningClusterName)
err = unjoiningClusterClientset.CoreV1().Namespaces().Delete(kubefedNamespace, &metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("The kubefed namespace %q no longer exists in unjoining cluster %q.", kubefedNamespace, unjoiningClusterName)
return nil
} else if err != nil {
return errors.Wrapf(err, "Could not delete kubefed namespace %q from unjoining cluster %q", kubefedNamespace, unjoiningClusterName)
} else {
klog.V(2).Infof("Deleted kubefed namespace %q from unjoining cluster %q.", kubefedNamespace, unjoiningClusterName)
}
return nil
}
// deleteServiceAccount deletes a service account in the cluster associated
// with clusterClientset with credentials that are used by the host cluster
// to access its API server.
func deleteServiceAccount(clusterClientset kubeclient.Interface, saName,
namespace, unjoiningClusterName string, dryRun bool) error {
if dryRun {
return nil
}
klog.V(2).Infof("Deleting service account \"%s/%s\" in unjoining cluster %q.", namespace, saName, unjoiningClusterName)
// Delete a service account.
err := clusterClientset.CoreV1().ServiceAccounts(namespace).Delete(saName,
&metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("Service account \"%s/%s\" does not exist.", namespace, saName)
} else if err != nil {
return errors.Wrapf(err, "Could not delete service account \"%s/%s\"", namespace, saName)
} else {
klog.V(2).Infof("Deleted service account \"%s/%s\" in unjoining cluster %q.", namespace, saName, unjoiningClusterName)
}
return nil
}
// deleteClusterRoleAndBinding deletes an RBAC cluster role and binding that
// allows the service account identified by saName to access all resources in
// all namespaces in the cluster associated with clusterClientset.
func deleteClusterRoleAndBinding(clusterClientset kubeclient.Interface,
saName, namespace, unjoiningClusterName string, forceDeletion, dryRun bool) error {
if dryRun {
return nil
}
roleName := util.RoleName(saName)
healthCheckRoleName := util.HealthCheckRoleName(saName, namespace)
// Attempt to delete all role and role bindings created by join
for _, name := range []string{roleName, healthCheckRoleName} {
klog.V(2).Infof("Deleting cluster role binding %q for service account %q in unjoining cluster %q.",
name, saName, unjoiningClusterName)
err := clusterClientset.RbacV1().ClusterRoleBindings().Delete(name, &metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("Cluster role binding %q for service account %q does not exist in unjoining cluster %q.",
name, saName, unjoiningClusterName)
} else if err != nil {
wrappedErr := errors.Wrapf(err, "Could not delete cluster role binding %q for service account %q in unjoining cluster %q",
name, saName, unjoiningClusterName)
if !forceDeletion {
return wrappedErr
}
klog.V(2).Infof("%v", wrappedErr)
} else {
klog.V(2).Infof("Deleted cluster role binding %q for service account %q in unjoining cluster %q.",
name, saName, unjoiningClusterName)
}
klog.V(2).Infof("Deleting cluster role %q for service account %q in unjoining cluster %q.",
name, saName, unjoiningClusterName)
err = clusterClientset.RbacV1().ClusterRoles().Delete(name, &metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("Cluster role %q for service account %q does not exist in unjoining cluster %q.",
name, saName, unjoiningClusterName)
} else if err != nil {
wrappedErr := errors.Wrapf(err, "Could not delete cluster role %q for service account %q in unjoining cluster %q",
name, saName, unjoiningClusterName)
if !forceDeletion {
return wrappedErr
}
klog.V(2).Infof("%v", wrappedErr)
} else {
klog.V(2).Infof("Deleted cluster role %q for service account %q in unjoining cluster %q.",
name, saName, unjoiningClusterName)
}
}
klog.V(2).Infof("Deleting role binding \"%s/%s\" for service account %q in unjoining cluster %q.",
namespace, roleName, saName, unjoiningClusterName)
err := clusterClientset.RbacV1().RoleBindings(namespace).Delete(roleName, &metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("Role binding \"%s/%s\" for service account %q does not exist in unjoining cluster %q.",
namespace, roleName, saName, unjoiningClusterName)
} else if err != nil {
wrappedErr := errors.Wrapf(err, "Could not delete role binding \"%s/%s\" for service account %q in unjoining cluster %q",
namespace, roleName, saName, unjoiningClusterName)
if !forceDeletion {
return wrappedErr
}
klog.V(2).Infof("%v", wrappedErr)
} else {
klog.V(2).Infof("Deleted role binding \"%s/%s\" for service account %q in unjoining cluster %q.",
namespace, roleName, saName, unjoiningClusterName)
}
klog.V(2).Infof("Deleting role \"%s/%s\" for service account %q in unjoining cluster %q.",
namespace, roleName, saName, unjoiningClusterName)
err = clusterClientset.RbacV1().Roles(namespace).Delete(roleName, &metav1.DeleteOptions{})
if apierrors.IsNotFound(err) {
klog.V(2).Infof("Role \"%s/%s\" for service account %q does not exist in unjoining cluster %q.",
namespace, roleName, saName, unjoiningClusterName)
} else if err != nil {
wrappedErr := errors.Wrapf(err, "Could not delete role \"%s/%s\" for service account %q in unjoining cluster %q",
namespace, roleName, saName, unjoiningClusterName)
if !forceDeletion {
return wrappedErr
}
klog.V(2).Infof("%v", wrappedErr)
} else {
klog.V(2).Infof("Deleting Role \"%s/%s\" for service account %q in unjoining cluster %q.",
namespace, roleName, saName, unjoiningClusterName)
}
return nil
}

View File

@@ -0,0 +1,166 @@
package cluster
import (
"fmt"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
kubeclient "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"strings"
)
// Default values for the federated group and version used by
// the enable and disable subcommands of `kubefedctl`.
const (
DefaultFederatedGroup = "types.kubefed.io"
DefaultFederatedVersion = "v1beta1"
FederatedKindPrefix = "Federated"
)
// FedConfig provides a rest config based on the filesystem kubeconfig (via
// pathOptions) and context in order to talk to the host kubernetes cluster
// and the joining kubernetes cluster.
type FedConfig interface {
HostConfig(context, kubeconfigPath string) (*rest.Config, error)
ClusterConfig(context, kubeconfigPath string) (*rest.Config, error)
GetClientConfig(ontext, kubeconfigPath string) clientcmd.ClientConfig
}
// fedConfig implements the FedConfig interface.
type fedConfig struct {
pathOptions *clientcmd.PathOptions
}
// NewFedConfig creates a fedConfig for `kubefedctl` commands.
func NewFedConfig(pathOptions *clientcmd.PathOptions) FedConfig {
return &fedConfig{
pathOptions: pathOptions,
}
}
// HostConfig provides a rest config to talk to the host kubernetes cluster
// based on the context and kubeconfig passed in.
func (a *fedConfig) HostConfig(context, kubeconfigPath string) (*rest.Config, error) {
hostConfig := a.GetClientConfig(context, kubeconfigPath)
hostClientConfig, err := hostConfig.ClientConfig()
if err != nil {
return nil, err
}
return hostClientConfig, nil
}
// ClusterConfig provides a rest config to talk to the joining kubernetes
// cluster based on the context and kubeconfig passed in.
func (a *fedConfig) ClusterConfig(context, kubeconfigPath string) (*rest.Config, error) {
clusterConfig := a.GetClientConfig(context, kubeconfigPath)
clusterClientConfig, err := clusterConfig.ClientConfig()
if err != nil {
return nil, err
}
return clusterClientConfig, nil
}
// getClientConfig is a helper method to create a client config from the
// context and kubeconfig passed as arguments.
func (a *fedConfig) GetClientConfig(context, kubeconfigPath string) clientcmd.ClientConfig {
loadingRules := *a.pathOptions.LoadingRules
loadingRules.Precedence = a.pathOptions.GetLoadingPrecedence()
loadingRules.ExplicitPath = kubeconfigPath
overrides := &clientcmd.ConfigOverrides{
CurrentContext: context,
}
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
}
// HostClientset provides a kubernetes API compliant clientset to
// communicate with the host cluster's kubernetes API server.
func HostClientset(config *rest.Config) (*kubeclient.Clientset, error) {
return kubeclient.NewForConfig(config)
}
// ClusterClientset provides a kubernetes API compliant clientset to
// communicate with the joining cluster's kubernetes API server.
func ClusterClientset(config *rest.Config) (*kubeclient.Clientset, error) {
return kubeclient.NewForConfig(config)
}
// ClusterServiceAccountName returns the name of a service account whose
// credentials are used by the host cluster to access the client cluster.
func ClusterServiceAccountName(joiningClusterName, hostClusterName string) string {
return fmt.Sprintf("%s-%s", joiningClusterName, hostClusterName)
}
// RoleName returns the name of a Role or ClusterRole and its
// associated RoleBinding or ClusterRoleBinding that are used to allow
// the service account to access necessary resources on the cluster.
func RoleName(serviceAccountName string) string {
return fmt.Sprintf("kubefed-controller-manager:%s", serviceAccountName)
}
// HealthCheckRoleName returns the name of a ClusterRole and its
// associated ClusterRoleBinding that is used to allow the service
// account to check the health of the cluster and list nodes.
func HealthCheckRoleName(serviceAccountName, namespace string) string {
return fmt.Sprintf("kubefed-controller-manager:%s:healthcheck-%s", namespace, serviceAccountName)
}
// IsFederatedAPIResource checks if a resource with the given Kind and group is a Federated one
func IsFederatedAPIResource(kind, group string) bool {
return strings.HasPrefix(kind, FederatedKindPrefix) && group == DefaultFederatedGroup
}
// GetNamespace returns namespace of the current context
func GetNamespace(hostClusterContext string, kubeconfig string, config FedConfig) (string, error) {
clientConfig := config.GetClientConfig(hostClusterContext, kubeconfig)
currentContext, err := CurrentContext(clientConfig)
if err != nil {
return "", err
}
ns, _, err := clientConfig.Namespace()
if err != nil {
return "", errors.Wrapf(err, "Failed to get ClientConfig for host cluster context %q and kubeconfig %q",
currentContext, kubeconfig)
}
if len(ns) == 0 {
ns = "default"
}
return ns, nil
}
// CurrentContext retrieves the current context from the provided config.
func CurrentContext(config clientcmd.ClientConfig) (string, error) {
rawConfig, err := config.RawConfig()
if err != nil {
return "", errors.Wrap(err, "Failed to get current context from config")
}
return rawConfig.CurrentContext, nil
}
// IsPrimaryCluster checks if the caller is working with objects for the
// primary cluster by checking if the UIDs match for both ObjectMetas passed
// in.
// TODO (font): Need to revisit this when cluster ID is available.
func IsPrimaryCluster(obj, clusterObj pkgruntime.Object) bool {
meta := MetaAccessor(obj)
clusterMeta := MetaAccessor(clusterObj)
return meta.GetUID() == clusterMeta.GetUID()
}
func MetaAccessor(obj pkgruntime.Object) metav1.Object {
accessor, err := meta.Accessor(obj)
if err != nil {
// This should always succeed if obj is not nil. Also,
// adapters are slated for replacement by unstructured.
return nil
}
return accessor
}

View File

@@ -0,0 +1,26 @@
package multicluster
import "github.com/spf13/pflag"
type Options struct {
// Enable
Enable bool `json:"enable"`
EnableFederation bool `json:"enableFederation,omitempty"`
}
// NewOptions() returns a default nil options
func NewOptions() *Options {
return &Options{
Enable: false,
EnableFederation: false,
}
}
func (o *Options) Validate() []error {
return nil
}
func (o *Options) AddFlags(fs *pflag.FlagSet, s *Options) {
fs.BoolVar(&o.Enable, "multiple-clusters", s.Enable, ""+
"This field instructs KubeSphere to enter multiple-cluster mode or not.")
}

201
vendor/k8s.io/kubectl/LICENSE generated vendored Normal file
View File

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

6
vendor/k8s.io/kubectl/pkg/util/openapi/OWNERS generated vendored Normal file
View File

@@ -0,0 +1,6 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- apelisse
reviewers:
- apelisse

21
vendor/k8s.io/kubectl/pkg/util/openapi/doc.go generated vendored Normal file
View File

@@ -0,0 +1,21 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package openapi is a collection of libraries for fetching the openapi spec
// from a Kubernetes server and then indexing the type definitions.
// The openapi spec contains the object model definitions and extensions metadata
// such as the patchStrategy and patchMergeKey for creating patches.
package openapi // k8s.io/kubectl/pkg/util/openapi

65
vendor/k8s.io/kubectl/pkg/util/openapi/dryrun.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openapi
import (
"errors"
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
yaml "gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool {
for _, extension := range extensions {
if extension.GetValue().GetYaml() == "" ||
extension.GetName() != "x-kubernetes-group-version-kind" {
continue
}
var value map[string]string
err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value)
if err != nil {
continue
}
if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version {
return true
}
return false
}
return false
}
// SupportsDryRun is a method that let's us look in the OpenAPI if the
// specific group-version-kind supports the dryRun query parameter for
// the PATCH end-point.
func SupportsDryRun(doc *openapi_v2.Document, gvk schema.GroupVersionKind) (bool, error) {
for _, path := range doc.GetPaths().GetPath() {
// Is this describing the gvk we're looking for?
if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) {
continue
}
for _, param := range path.GetValue().GetPatch().GetParameters() {
if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == "dryRun" {
return true, nil
}
}
return false, nil
}
return false, errors.New("couldn't find GVK in openapi")
}

27
vendor/k8s.io/kubectl/pkg/util/openapi/extensions.go generated vendored Normal file
View File

@@ -0,0 +1,27 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openapi
import "github.com/go-openapi/spec"
// PrintColumnsKey is the key that defines which columns should be printed
const PrintColumnsKey = "x-kubernetes-print-columns"
// GetPrintColumns looks for the open API extension for the display columns.
func GetPrintColumns(extensions spec.Extensions) (string, bool) {
return extensions.GetString(PrintColumnsKey)
}

128
vendor/k8s.io/kubectl/pkg/util/openapi/openapi.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openapi
import (
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kube-openapi/pkg/util/proto"
)
// Resources interface describe a resources provider, that can give you
// resource based on group-version-kind.
type Resources interface {
LookupResource(gvk schema.GroupVersionKind) proto.Schema
}
// groupVersionKindExtensionKey is the key used to lookup the
// GroupVersionKind value for an object definition from the
// definition's "extensions" map.
const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"
// document is an implementation of `Resources`. It looks for
// resources in an openapi Schema.
type document struct {
// Maps gvk to model name
resources map[schema.GroupVersionKind]string
models proto.Models
}
var _ Resources = &document{}
// NewOpenAPIData creates a new `Resources` out of the openapi document
func NewOpenAPIData(doc *openapi_v2.Document) (Resources, error) {
models, err := proto.NewOpenAPIData(doc)
if err != nil {
return nil, err
}
resources := map[schema.GroupVersionKind]string{}
for _, modelName := range models.ListModels() {
model := models.LookupModel(modelName)
if model == nil {
panic("ListModels returns a model that can't be looked-up.")
}
gvkList := parseGroupVersionKind(model)
for _, gvk := range gvkList {
if len(gvk.Kind) > 0 {
resources[gvk] = modelName
}
}
}
return &document{
resources: resources,
models: models,
}, nil
}
func (d *document) LookupResource(gvk schema.GroupVersionKind) proto.Schema {
modelName, found := d.resources[gvk]
if !found {
return nil
}
return d.models.LookupModel(modelName)
}
// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
extensions := s.GetExtensions()
gvkListResult := []schema.GroupVersionKind{}
// Get the extensions
gvkExtension, ok := extensions[groupVersionKindExtensionKey]
if !ok {
return []schema.GroupVersionKind{}
}
// gvk extension must be a list of at least 1 element.
gvkList, ok := gvkExtension.([]interface{})
if !ok {
return []schema.GroupVersionKind{}
}
for _, gvk := range gvkList {
// gvk extension list must be a map with group, version, and
// kind fields
gvkMap, ok := gvk.(map[interface{}]interface{})
if !ok {
continue
}
group, ok := gvkMap["group"].(string)
if !ok {
continue
}
version, ok := gvkMap["version"].(string)
if !ok {
continue
}
kind, ok := gvkMap["kind"].(string)
if !ok {
continue
}
gvkListResult = append(gvkListResult, schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
})
}
return gvkListResult
}

View File

@@ -0,0 +1,65 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openapi
import (
"sync"
"k8s.io/client-go/discovery"
)
// synchronizedOpenAPIGetter fetches the openapi schema once and then caches it in memory
type synchronizedOpenAPIGetter struct {
// Cached results
sync.Once
openAPISchema Resources
err error
openAPIClient discovery.OpenAPISchemaInterface
}
var _ Getter = &synchronizedOpenAPIGetter{}
// Getter is an interface for fetching openapi specs and parsing them into an Resources struct
type Getter interface {
// OpenAPIData returns the parsed OpenAPIData
Get() (Resources, error)
}
// NewOpenAPIGetter returns an object to return OpenAPIDatas which reads
// from a server, and then stores in memory for subsequent invocations
func NewOpenAPIGetter(openAPIClient discovery.OpenAPISchemaInterface) Getter {
return &synchronizedOpenAPIGetter{
openAPIClient: openAPIClient,
}
}
// Resources implements Getter
func (g *synchronizedOpenAPIGetter) Get() (Resources, error) {
g.Do(func() {
s, err := g.openAPIClient.OpenAPISchema()
if err != nil {
g.err = err
return
}
g.openAPISchema, g.err = NewOpenAPIData(s)
})
// Return the save result
return g.openAPISchema, g.err
}

52
vendor/modules.txt vendored
View File

@@ -7,9 +7,9 @@ github.com/Azure/go-ansiterm
github.com/Azure/go-ansiterm/winterm
# github.com/Azure/go-autorest/autorest v0.9.0 => github.com/Azure/go-autorest/autorest v0.9.0
github.com/Azure/go-autorest/autorest
github.com/Azure/go-autorest/autorest/azure
# github.com/Azure/go-autorest/autorest/adal v0.5.0 => github.com/Azure/go-autorest/autorest/adal v0.5.0
github.com/Azure/go-autorest/autorest/adal
github.com/Azure/go-autorest/autorest/azure
# github.com/Azure/go-autorest/autorest/date v0.1.0 => github.com/Azure/go-autorest/autorest/date v0.1.0
github.com/Azure/go-autorest/autorest/date
# github.com/Azure/go-autorest/logger v0.1.0 => github.com/Azure/go-autorest/logger v0.1.0
@@ -166,9 +166,9 @@ github.com/elastic/go-elasticsearch/v7/estransport
github.com/elastic/go-elasticsearch/v7/internal/version
# github.com/emicklei/go-restful v2.9.5+incompatible => github.com/emicklei/go-restful v2.9.5+incompatible
github.com/emicklei/go-restful
github.com/emicklei/go-restful/log
# github.com/emicklei/go-restful-openapi v1.0.0 => github.com/emicklei/go-restful-openapi v1.0.0
github.com/emicklei/go-restful-openapi
github.com/emicklei/go-restful/log
# github.com/emirpasic/gods v1.12.0 => github.com/emirpasic/gods v1.12.0
github.com/emirpasic/gods/containers
github.com/emirpasic/gods/lists
@@ -345,7 +345,7 @@ github.com/inconshreveable/mousetrap
github.com/jbenet/go-context/io
# github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af => github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
github.com/jmespath/go-jmespath
# github.com/json-iterator/go v1.1.8 => github.com/json-iterator/go v1.1.8
# github.com/json-iterator/go v1.1.9 => github.com/json-iterator/go v1.1.8
github.com/json-iterator/go
# github.com/kelseyhightower/envconfig v1.4.0 => github.com/kelseyhightower/envconfig v1.4.0
github.com/kelseyhightower/envconfig
@@ -421,7 +421,7 @@ github.com/modern-go/reflect2
github.com/munnerz/goautoneg
# github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f => github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f
github.com/mxk/go-flowrate/flowrate
# github.com/onsi/ginkgo v1.8.0 => github.com/onsi/ginkgo v1.8.0
# github.com/onsi/ginkgo v1.12.0 => github.com/onsi/ginkgo v1.8.0
github.com/onsi/ginkgo
github.com/onsi/ginkgo/config
github.com/onsi/ginkgo/extensions/table
@@ -441,7 +441,7 @@ github.com/onsi/ginkgo/reporters/stenographer
github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable
github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty
github.com/onsi/ginkgo/types
# github.com/onsi/gomega v1.5.0 => github.com/onsi/gomega v1.5.0
# github.com/onsi/gomega v1.9.0 => github.com/onsi/gomega v1.5.0
github.com/onsi/gomega
github.com/onsi/gomega/format
github.com/onsi/gomega/gbytes
@@ -510,7 +510,7 @@ github.com/pborman/uuid
github.com/pelletier/go-buffruneio
# github.com/pelletier/go-toml v1.2.0 => github.com/pelletier/go-toml v1.2.0
github.com/pelletier/go-toml
# github.com/pkg/errors v0.8.1 => github.com/pkg/errors v0.8.1
# github.com/pkg/errors v0.9.1 => github.com/pkg/errors v0.8.1
github.com/pkg/errors
# github.com/pmezard/go-difflib v1.0.0 => github.com/pmezard/go-difflib v1.0.0
github.com/pmezard/go-difflib/difflib
@@ -549,7 +549,7 @@ github.com/projectcalico/libcalico-go/lib/selector/tokenizer
github.com/projectcalico/libcalico-go/lib/set
github.com/projectcalico/libcalico-go/lib/validator/v3
github.com/projectcalico/libcalico-go/lib/watch
# github.com/prometheus/client_golang v0.9.4 => github.com/prometheus/client_golang v0.9.4
# github.com/prometheus/client_golang v1.0.0 => github.com/prometheus/client_golang v0.9.4
github.com/prometheus/client_golang/api
github.com/prometheus/client_golang/api/prometheus/v1
github.com/prometheus/client_golang/prometheus
@@ -847,7 +847,7 @@ gopkg.in/src-d/go-git.v4/utils/merkletrie/noder
gopkg.in/tomb.v1
# gopkg.in/warnings.v0 v0.1.2 => gopkg.in/warnings.v0 v0.1.2
gopkg.in/warnings.v0
# gopkg.in/yaml.v2 v2.2.4 => gopkg.in/yaml.v2 v2.2.4
# gopkg.in/yaml.v2 v2.2.8 => gopkg.in/yaml.v2 v2.2.4
gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 => gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966
gopkg.in/yaml.v3
@@ -899,7 +899,7 @@ istio.io/client-go/pkg/listers/security/v1beta1
# istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a => istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a
istio.io/gogo-genproto/googleapis/google/api
istio.io/gogo-genproto/googleapis/google/rpc
# k8s.io/api v0.0.0-20191114100352-16d7abae0d2a => k8s.io/api v0.0.0-20191114100352-16d7abae0d2a
# k8s.io/api v0.17.3 => k8s.io/api v0.0.0-20191114100352-16d7abae0d2a
k8s.io/api/admission/v1
k8s.io/api/admission/v1beta1
k8s.io/api/admissionregistration/v1
@@ -940,7 +940,7 @@ k8s.io/api/settings/v1alpha1
k8s.io/api/storage/v1
k8s.io/api/storage/v1alpha1
k8s.io/api/storage/v1beta1
# k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833 => k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833
# k8s.io/apiextensions-apiserver v0.17.3 => k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1
@@ -948,7 +948,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1
# k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb => k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb
# k8s.io/apimachinery v0.17.3 => k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb
k8s.io/apimachinery/pkg/api/equality
k8s.io/apimachinery/pkg/api/errors
k8s.io/apimachinery/pkg/api/meta
@@ -1004,7 +1004,7 @@ k8s.io/apimachinery/pkg/watch
k8s.io/apimachinery/third_party/forked/golang/json
k8s.io/apimachinery/third_party/forked/golang/netutil
k8s.io/apimachinery/third_party/forked/golang/reflect
# k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 => k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682
# k8s.io/apiserver v0.17.3 => k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682
k8s.io/apiserver/pkg/admission
k8s.io/apiserver/pkg/admission/configuration
k8s.io/apiserver/pkg/admission/initializer
@@ -1108,7 +1108,7 @@ k8s.io/apiserver/plugin/pkg/audit/truncate
k8s.io/apiserver/plugin/pkg/audit/webhook
k8s.io/apiserver/plugin/pkg/authenticator/token/webhook
k8s.io/apiserver/plugin/pkg/authorizer/webhook
# k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
# k8s.io/client-go v0.17.3 => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
k8s.io/client-go/discovery
k8s.io/client-go/discovery/fake
k8s.io/client-go/dynamic
@@ -1319,7 +1319,7 @@ k8s.io/client-go/util/jsonpath
k8s.io/client-go/util/keyutil
k8s.io/client-go/util/retry
k8s.io/client-go/util/workqueue
# k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 => k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894
# k8s.io/code-generator v0.17.3 => k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894
k8s.io/code-generator/cmd/client-gen
k8s.io/code-generator/cmd/client-gen/args
k8s.io/code-generator/cmd/client-gen/generators
@@ -1338,7 +1338,7 @@ k8s.io/code-generator/cmd/lister-gen/args
k8s.io/code-generator/cmd/lister-gen/generators
k8s.io/code-generator/pkg/namer
k8s.io/code-generator/pkg/util
# k8s.io/component-base v0.0.0-20191114102325-35a9586014f7 => k8s.io/component-base v0.0.0-20191114102325-35a9586014f7
# k8s.io/component-base v0.17.3 => k8s.io/component-base v0.0.0-20191114102325-35a9586014f7
k8s.io/component-base/cli/flag
k8s.io/component-base/featuregate
k8s.io/component-base/logs
@@ -1368,6 +1368,8 @@ k8s.io/kube-openapi/pkg/schemaconv
k8s.io/kube-openapi/pkg/util
k8s.io/kube-openapi/pkg/util/proto
k8s.io/kube-openapi/pkg/util/sets
# k8s.io/kubectl v0.17.3 => k8s.io/kubectl v0.17.3
k8s.io/kubectl/pkg/util/openapi
# k8s.io/utils v0.0.0-20191114184206-e782cd3c129f => k8s.io/utils v0.0.0-20191114184206-e782cd3c129f
k8s.io/utils/buffer
k8s.io/utils/integer
@@ -1389,7 +1391,7 @@ openpitrix.io/openpitrix/pkg/util/reflectutil
openpitrix.io/openpitrix/pkg/util/stringutil
openpitrix.io/openpitrix/pkg/util/yamlutil
openpitrix.io/openpitrix/pkg/version
# sigs.k8s.io/controller-runtime v0.4.0 => sigs.k8s.io/controller-runtime v0.4.0
# sigs.k8s.io/controller-runtime v0.5.0 => sigs.k8s.io/controller-runtime v0.4.0
sigs.k8s.io/controller-runtime/pkg/cache
sigs.k8s.io/controller-runtime/pkg/cache/internal
sigs.k8s.io/controller-runtime/pkg/client
@@ -1439,6 +1441,24 @@ sigs.k8s.io/controller-tools/pkg/schemapatcher
sigs.k8s.io/controller-tools/pkg/schemapatcher/internal/yaml
sigs.k8s.io/controller-tools/pkg/version
sigs.k8s.io/controller-tools/pkg/webhook
# sigs.k8s.io/kubefed v0.2.0-alpha.1 => sigs.k8s.io/kubefed v0.2.0-alpha.1
sigs.k8s.io/kubefed/pkg/apis
sigs.k8s.io/kubefed/pkg/apis/core/common
sigs.k8s.io/kubefed/pkg/apis/core/typeconfig
sigs.k8s.io/kubefed/pkg/apis/core/v1alpha1
sigs.k8s.io/kubefed/pkg/apis/core/v1beta1
sigs.k8s.io/kubefed/pkg/apis/multiclusterdns/v1alpha1
sigs.k8s.io/kubefed/pkg/apis/scheduling/v1alpha1
sigs.k8s.io/kubefed/pkg/client/generic
sigs.k8s.io/kubefed/pkg/client/generic/scheme
sigs.k8s.io/kubefed/pkg/controller/util
sigs.k8s.io/kubefed/pkg/kubefedctl
sigs.k8s.io/kubefed/pkg/kubefedctl/enable
sigs.k8s.io/kubefed/pkg/kubefedctl/federate
sigs.k8s.io/kubefed/pkg/kubefedctl/options
sigs.k8s.io/kubefed/pkg/kubefedctl/orphaning
sigs.k8s.io/kubefed/pkg/kubefedctl/util
sigs.k8s.io/kubefed/pkg/version
# sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca => sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca
sigs.k8s.io/structured-merge-diff/fieldpath
sigs.k8s.io/structured-merge-diff/merge

201
vendor/sigs.k8s.io/kubefed/LICENSE generated vendored Normal file
View File

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

View File

@@ -0,0 +1,26 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"sigs.k8s.io/kubefed/pkg/apis/core/v1alpha1"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
}

View File

@@ -0,0 +1,26 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1beta1.SchemeBuilder.AddToScheme)
}

View File

@@ -0,0 +1,26 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"sigs.k8s.io/kubefed/pkg/apis/multiclusterdns/v1alpha1"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
}

View File

@@ -0,0 +1,26 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"sigs.k8s.io/kubefed/pkg/apis/scheduling/v1alpha1"
)
func init() {
// Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
}

30
vendor/sigs.k8s.io/kubefed/pkg/apis/apis.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package apis contains Kubernetes API groups.
package apis
import (
"k8s.io/apimachinery/pkg/runtime"
)
// AddToSchemes may be used to add all resources defined in the project to a Scheme
var AddToSchemes runtime.SchemeBuilder
// AddToScheme adds all Resources to the Scheme
func AddToScheme(s *runtime.Scheme) error {
return AddToSchemes.AddToScheme(s)
}

View File

@@ -0,0 +1,31 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
type ClusterConditionType string
// These are valid conditions of a cluster.
const (
// ClusterReady means the cluster is ready to accept workloads.
ClusterReady ClusterConditionType = "Ready"
// ClusterOffline means the cluster is temporarily down or not reachable
ClusterOffline ClusterConditionType = "Offline"
)
const (
NamespaceName = "namespaces"
)

View File

@@ -0,0 +1,30 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
import (
"fmt"
"strings"
)
func PropagatedVersionName(kind, resourceName string) string {
return fmt.Sprintf("%s%s", PropagatedVersionPrefix(kind), resourceName)
}
func PropagatedVersionPrefix(kind string) string {
return fmt.Sprintf("%s-", strings.ToLower(kind))
}

View File

@@ -0,0 +1,34 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package typeconfig
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Interface defines how to interact with a FederatedTypeConfig
type Interface interface {
GetObjectMeta() metav1.ObjectMeta
GetTargetType() metav1.APIResource
GetNamespaced() bool
GetPropagationEnabled() bool
GetFederatedType() metav1.APIResource
GetStatusType() *metav1.APIResource
GetStatusEnabled() bool
GetFederatedNamespaced() bool
IsNamespace() bool
}

View File

@@ -0,0 +1,39 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package typeconfig
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// GroupQualifiedName returns the plural name of the api resource
// optionally qualified by its group:
//
// '<target plural name>[.<target group name>]'
//
// This is the naming scheme for FederatedTypeConfig resources. The
// scheme ensures that, for a given KubeFed control plane,
// federation of a target type will be configured by at most one
// FederatedTypeConfig.
func GroupQualifiedName(apiResource metav1.APIResource) string {
if len(apiResource.Group) == 0 {
return apiResource.Name
}
return fmt.Sprintf("%s.%s", apiResource.Name, apiResource.Group)
}

View File

@@ -0,0 +1,59 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ClusterPropagatedVersionSpec defines the desired state of ClusterPropagatedVersion
type ClusterPropagatedVersionSpec struct {
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=clusterpropagatedversions,scope=Cluster
// +kubebuilder:subresource:status
// ClusterPropagatedVersion holds version information about the state
// propagated from KubeFed APIs (configured by FederatedTypeConfig
// resources) to member clusters. The name of a ClusterPropagatedVersion
// encodes the kind and name of the resource it stores information for
// (i.e. <lower-case kind>-<resource name>). If a target resource has
// a populated metadata.Generation field, the generation will be
// stored with a prefix of `gen:` as the version for the cluster. If
// metadata.Generation is not available, metadata.ResourceVersion will
// be stored with a prefix of `rv:` as the version for the cluster.
type ClusterPropagatedVersion struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
Status PropagatedVersionStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ClusterPropagatedVersionList contains a list of ClusterPropagatedVersion
type ClusterPropagatedVersionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ClusterPropagatedVersion `json:"items"`
}
func init() {
SchemeBuilder.Register(&ClusterPropagatedVersion{}, &ClusterPropagatedVersionList{})
}

View File

@@ -0,0 +1,52 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// FederatedServiceClusterStatus is the observed status of the resource for a named cluster
type FederatedServiceClusterStatus struct {
ClusterName string `json:"clusterName"`
Status corev1.ServiceStatus `json:"status"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=federatedservicestatuses
type FederatedServiceStatus struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
ClusterStatus []FederatedServiceClusterStatus `json:"clusterStatus,omitempty"`
}
// +kubebuilder:object:root=true
// FederatedServiceStatusList contains a list of FederatedServiceStatus
type FederatedServiceStatusList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []FederatedServiceStatus `json:"items"`
}
func init() {
SchemeBuilder.Register(&FederatedServiceStatus{}, &FederatedServiceStatusList{})
}

View File

@@ -0,0 +1,38 @@
/*
Copyright 2019 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.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1alpha1 contains API Schema definitions for the core v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=core.kubefed.io
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "core.kubefed.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@@ -0,0 +1,78 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// PropagatedVersionSpec defines the desired state of PropagatedVersion
type PropagatedVersionSpec struct {
}
// PropagatedVersionStatus defines the observed state of PropagatedVersion
type PropagatedVersionStatus struct {
// The observed version of the template for this resource.
TemplateVersion string `json:"templateVersion"`
// The observed version of the overrides for this resource.
OverrideVersion string `json:"overridesVersion"`
// The last versions produced in each cluster for this resource.
// +optional
ClusterVersions []ClusterObjectVersion `json:"clusterVersions,omitempty"`
}
type ClusterObjectVersion struct {
// The name of the cluster the version is for.
ClusterName string `json:"clusterName"`
// The last version produced for the resource by a KubeFed
// operation.
Version string `json:"version"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=propagatedversions
// +kubebuilder:subresource:status
// PropagatedVersion holds version information about the state
// propagated from KubeFed APIs (configured by FederatedTypeConfig
// resources) to member clusters. The name of a PropagatedVersion
// encodes the kind and name of the resource it stores information for
// (i.e. <lower-case kind>-<resource name>). If a target resource has
// a populated metadata.Generation field, the generation will be
// stored with a prefix of `gen:` as the version for the cluster. If
// metadata.Generation is not available, metadata.ResourceVersion will
// be stored with a prefix of `rv:` as the version for the cluster.
type PropagatedVersion struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
Status PropagatedVersionStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// PropagatedVersionList contains a list of PropagatedVersion
type PropagatedVersionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []PropagatedVersion `json:"items"`
}
func init() {
SchemeBuilder.Register(&PropagatedVersion{}, &PropagatedVersionList{})
}

View File

@@ -0,0 +1,286 @@
// +build !ignore_autogenerated
/*
Copyright 2018 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 controller-gen. DO NOT EDIT.
package v1alpha1
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 *ClusterObjectVersion) DeepCopyInto(out *ClusterObjectVersion) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectVersion.
func (in *ClusterObjectVersion) DeepCopy() *ClusterObjectVersion {
if in == nil {
return nil
}
out := new(ClusterObjectVersion)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterPropagatedVersion) DeepCopyInto(out *ClusterPropagatedVersion) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPropagatedVersion.
func (in *ClusterPropagatedVersion) DeepCopy() *ClusterPropagatedVersion {
if in == nil {
return nil
}
out := new(ClusterPropagatedVersion)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterPropagatedVersion) 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 *ClusterPropagatedVersionList) DeepCopyInto(out *ClusterPropagatedVersionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterPropagatedVersion, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPropagatedVersionList.
func (in *ClusterPropagatedVersionList) DeepCopy() *ClusterPropagatedVersionList {
if in == nil {
return nil
}
out := new(ClusterPropagatedVersionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterPropagatedVersionList) 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 *ClusterPropagatedVersionSpec) DeepCopyInto(out *ClusterPropagatedVersionSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPropagatedVersionSpec.
func (in *ClusterPropagatedVersionSpec) DeepCopy() *ClusterPropagatedVersionSpec {
if in == nil {
return nil
}
out := new(ClusterPropagatedVersionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedServiceClusterStatus) DeepCopyInto(out *FederatedServiceClusterStatus) {
*out = *in
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedServiceClusterStatus.
func (in *FederatedServiceClusterStatus) DeepCopy() *FederatedServiceClusterStatus {
if in == nil {
return nil
}
out := new(FederatedServiceClusterStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedServiceStatus) DeepCopyInto(out *FederatedServiceStatus) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.ClusterStatus != nil {
in, out := &in.ClusterStatus, &out.ClusterStatus
*out = make([]FederatedServiceClusterStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedServiceStatus.
func (in *FederatedServiceStatus) DeepCopy() *FederatedServiceStatus {
if in == nil {
return nil
}
out := new(FederatedServiceStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedServiceStatus) 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 *FederatedServiceStatusList) DeepCopyInto(out *FederatedServiceStatusList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]FederatedServiceStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedServiceStatusList.
func (in *FederatedServiceStatusList) DeepCopy() *FederatedServiceStatusList {
if in == nil {
return nil
}
out := new(FederatedServiceStatusList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedServiceStatusList) 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 *PropagatedVersion) DeepCopyInto(out *PropagatedVersion) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PropagatedVersion.
func (in *PropagatedVersion) DeepCopy() *PropagatedVersion {
if in == nil {
return nil
}
out := new(PropagatedVersion)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PropagatedVersion) 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 *PropagatedVersionList) DeepCopyInto(out *PropagatedVersionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]PropagatedVersion, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PropagatedVersionList.
func (in *PropagatedVersionList) DeepCopy() *PropagatedVersionList {
if in == nil {
return nil
}
out := new(PropagatedVersionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PropagatedVersionList) 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 *PropagatedVersionSpec) DeepCopyInto(out *PropagatedVersionSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PropagatedVersionSpec.
func (in *PropagatedVersionSpec) DeepCopy() *PropagatedVersionSpec {
if in == nil {
return nil
}
out := new(PropagatedVersionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PropagatedVersionStatus) DeepCopyInto(out *PropagatedVersionStatus) {
*out = *in
if in.ClusterVersions != nil {
in, out := &in.ClusterVersions, &out.ClusterVersions
*out = make([]ClusterObjectVersion, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PropagatedVersionStatus.
func (in *PropagatedVersionStatus) DeepCopy() *PropagatedVersionStatus {
if in == nil {
return nil
}
out := new(PropagatedVersionStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -0,0 +1,257 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
"fmt"
"strings"
apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kubefed/pkg/apis/core/common"
)
// FederatedTypeConfigSpec defines the desired state of FederatedTypeConfig.
type FederatedTypeConfigSpec struct {
// The configuration of the target type. If not set, the pluralName and
// groupName fields will be set from the metadata.name of this resource. The
// kind field must be set.
TargetType APIResource `json:"targetType"`
// Whether or not propagation to member clusters should be enabled.
Propagation PropagationMode `json:"propagation"`
// Configuration for the federated type that defines (via
// template, placement and overrides fields) how the target type
// should appear in multiple cluster.
FederatedType APIResource `json:"federatedType"`
// Configuration for the status type that holds information about which type
// holds the status of the federated resource. If not provided, the group
// and version will default to those provided for the federated type api
// resource.
// +optional
StatusType *APIResource `json:"statusType,omitempty"`
// Whether or not Status object should be populated.
// +optional
StatusCollection *StatusCollectionMode `json:"statusCollection,omitempty"`
}
// APIResource defines how to configure the dynamic client for an API resource.
type APIResource struct {
// metav1.GroupVersion is not used since the json annotation of
// the fields enforces them as mandatory.
// Group of the resource.
// +optional
Group string `json:"group,omitempty"`
// Version of the resource.
Version string `json:"version"`
// Camel-cased singular name of the resource (e.g. ConfigMap)
Kind string `json:"kind"`
// Lower-cased plural name of the resource (e.g. configmaps). If
// not provided, it will be computed by lower-casing the kind and
// suffixing an 's'.
PluralName string `json:"pluralName"`
// Scope of the resource.
Scope apiextv1b1.ResourceScope `json:"scope"`
}
// PropagationMode defines the state of propagation to member clusters.
type PropagationMode string
const (
PropagationEnabled PropagationMode = "Enabled"
PropagationDisabled PropagationMode = "Disabled"
)
// StatusCollectionMode defines the state of status collection.
type StatusCollectionMode string
const (
StatusCollectionEnabled StatusCollectionMode = "Enabled"
StatusCollectionDisabled StatusCollectionMode = "Disabled"
)
// ControllerStatus defines the current state of the controller
type ControllerStatus string
const (
// ControllerStatusRunning means controller is in "running" state
ControllerStatusRunning ControllerStatus = "Running"
// ControllerStatusNotRunning means controller is in "notrunning" state
ControllerStatusNotRunning ControllerStatus = "NotRunning"
)
// FederatedTypeConfigStatus defines the observed state of FederatedTypeConfig
type FederatedTypeConfigStatus struct {
// ObservedGeneration is the generation as observed by the controller consuming the FederatedTypeConfig.
ObservedGeneration int64 `json:"observedGeneration"`
// PropagationController tracks the status of the sync controller.
PropagationController ControllerStatus `json:"propagationController"`
// StatusController tracks the status of the status controller.
// +optional
StatusController *ControllerStatus `json:"statusController,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=federatedtypeconfigs,shortName=ftc
// +kubebuilder:subresource:status
// FederatedTypeConfig programs KubeFed to know about a single API type - the
// "target type" - that a user wants to federate. For each target type, there is
// a corresponding FederatedType that has the following fields:
//
// - The "template" field specifies the basic definition of a federated resource
// - The "placement" field specifies the placement information for the federated
// resource
// - The "overrides" field specifies how the target resource should vary across
// clusters.
type FederatedTypeConfig struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec FederatedTypeConfigSpec `json:"spec"`
// +optional
Status FederatedTypeConfigStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// FederatedTypeConfigList contains a list of FederatedTypeConfig
type FederatedTypeConfigList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []FederatedTypeConfig `json:"items"`
}
func init() {
SchemeBuilder.Register(&FederatedTypeConfig{}, &FederatedTypeConfigList{})
}
func SetFederatedTypeConfigDefaults(obj *FederatedTypeConfig) {
// TODO(marun) will name always be populated?
nameParts := strings.SplitN(obj.Name, ".", 2)
targetPluralName := nameParts[0]
setStringDefault(&obj.Spec.TargetType.PluralName, targetPluralName)
if len(nameParts) > 1 {
group := nameParts[1]
setStringDefault(&obj.Spec.TargetType.Group, group)
}
setStringDefault(&obj.Spec.FederatedType.PluralName, PluralName(obj.Spec.FederatedType.Kind))
if obj.Spec.StatusType != nil {
setStringDefault(&obj.Spec.StatusType.PluralName, PluralName(obj.Spec.StatusType.Kind))
setStringDefault(&obj.Spec.StatusType.Group, obj.Spec.FederatedType.Group)
setStringDefault(&obj.Spec.StatusType.Version, obj.Spec.FederatedType.Version)
}
}
// GetDefaultedString returns the value if provided, and otherwise
// returns the provided default.
func setStringDefault(value *string, defaultValue string) {
if value == nil || len(*value) > 0 {
return
}
*value = defaultValue
}
// PluralName computes the plural name from the kind by
// lowercasing and suffixing with 's' or `es`.
func PluralName(kind string) string {
lowerKind := strings.ToLower(kind)
if strings.HasSuffix(lowerKind, "s") || strings.HasSuffix(lowerKind, "x") ||
strings.HasSuffix(lowerKind, "ch") || strings.HasSuffix(lowerKind, "sh") ||
strings.HasSuffix(lowerKind, "z") || strings.HasSuffix(lowerKind, "o") {
return fmt.Sprintf("%ses", lowerKind)
}
if strings.HasSuffix(lowerKind, "y") {
lowerKind = strings.TrimSuffix(lowerKind, "y")
return fmt.Sprintf("%sies", lowerKind)
}
return fmt.Sprintf("%ss", lowerKind)
}
func (f *FederatedTypeConfig) GetObjectMeta() metav1.ObjectMeta {
return f.ObjectMeta
}
func (f *FederatedTypeConfig) GetTargetType() metav1.APIResource {
return apiResourceToMeta(f.Spec.TargetType, f.GetNamespaced())
}
// TODO(font): This method should be removed from the interface in favor of
// checking the namespaced property of the appropriate APIResource (TargetType,
// FederatedType) depending on context.
func (f *FederatedTypeConfig) GetNamespaced() bool {
return f.Spec.TargetType.Namespaced()
}
func (f *FederatedTypeConfig) GetPropagationEnabled() bool {
return f.Spec.Propagation == PropagationEnabled
}
func (f *FederatedTypeConfig) GetFederatedType() metav1.APIResource {
return apiResourceToMeta(f.Spec.FederatedType, f.GetFederatedNamespaced())
}
func (f *FederatedTypeConfig) GetStatusType() *metav1.APIResource {
if f.Spec.StatusType == nil {
return nil
}
metaAPIResource := apiResourceToMeta(*f.Spec.StatusType, f.Spec.StatusType.Namespaced())
return &metaAPIResource
}
func (f *FederatedTypeConfig) GetStatusEnabled() bool {
return f.Spec.StatusCollection != nil &&
*f.Spec.StatusCollection == StatusCollectionEnabled &&
f.Name == "services"
}
// TODO(font): This method should be removed from the interface i.e. remove
// special-case handling for namespaces, in favor of checking the namespaced
// property of the appropriate APIResource (TargetType, FederatedType)
// depending on context.
func (f *FederatedTypeConfig) GetFederatedNamespaced() bool {
// Special-case the scope of federated namespace since it will
// hopefully be the only instance of the scope of a federated
// type differing from the scope of its target.
if f.IsNamespace() {
// FederatedNamespace is namespaced to allow the control plane to run
// with only namespace-scoped permissions e.g. to determine placement.
return true
}
return f.GetNamespaced()
}
func (f *FederatedTypeConfig) IsNamespace() bool {
return f.Name == common.NamespaceName
}
func (a *APIResource) Namespaced() bool {
return a.Scope == apiextv1b1.NamespaceScoped
}
func apiResourceToMeta(apiResource APIResource, namespaced bool) metav1.APIResource {
return metav1.APIResource{
Group: apiResource.Group,
Version: apiResource.Version,
Kind: apiResource.Kind,
Name: apiResource.PluralName,
Namespaced: namespaced,
}
}

View File

@@ -0,0 +1,38 @@
/*
Copyright 2019 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.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1beta1 contains API Schema definitions for the core v1beta1 API group
// +kubebuilder:object:generate=true
// +groupName=core.kubefed.io
package v1beta1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "core.kubefed.io", Version: "v1beta1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@@ -0,0 +1,125 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kubefed/pkg/apis/core/common"
)
type TLSValidation string
const (
TLSAll TLSValidation = "*"
TLSSubjectName TLSValidation = "SubjectName"
TLSValidityPeriod TLSValidation = "ValidityPeriod"
)
// KubeFedClusterSpec defines the desired state of KubeFedCluster
type KubeFedClusterSpec struct {
// The API endpoint of the member cluster. This can be a hostname,
// hostname:port, IP or IP:port.
APIEndpoint string `json:"apiEndpoint"`
// CABundle contains the certificate authority information.
// +optional
CABundle []byte `json:"caBundle,omitempty"`
// Name of the secret containing the token required to access the
// member cluster. The secret needs to exist in the same namespace
// as the control plane and should have a "token" key.
SecretRef LocalSecretReference `json:"secretRef"`
// DisabledTLSValidations defines a list of checks to ignore when validating
// the TLS connection to the member cluster. This can be any of *, SubjectName, or ValidityPeriod.
// If * is specified, it is expected to be the only option in list.
// +optional
DisabledTLSValidations []TLSValidation `json:"disabledTLSValidations,omitempty"`
}
// LocalSecretReference is a reference to a secret within the enclosing
// namespace.
type LocalSecretReference struct {
// Name of a secret within the enclosing
// namespace
Name string `json:"name"`
}
// KubeFedClusterStatus contains information about the current status of a
// cluster updated periodically by cluster controller.
type KubeFedClusterStatus struct {
// Conditions is an array of current cluster conditions.
Conditions []ClusterCondition `json:"conditions"`
// Zones are the names of availability zones in which the nodes of the cluster exist, e.g. 'us-east1-a'.
// +optional
Zones []string `json:"zones,omitempty"`
// Region is the name of the region in which all of the nodes in the cluster exist. e.g. 'us-east1'.
// +optional
Region *string `json:"region,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:name=age,type=date,JSONPath=.metadata.creationTimestamp
// +kubebuilder:printcolumn:name=ready,type=string,JSONPath=.status.conditions[?(@.type=='Ready')].status
// +kubebuilder:resource:path=kubefedclusters
// +kubebuilder:subresource:status
// KubeFedCluster configures KubeFed to be aware of a Kubernetes
// cluster and encapsulates the details necessary to communicate with
// the cluster.
type KubeFedCluster struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KubeFedClusterSpec `json:"spec"`
// +optional
Status KubeFedClusterStatus `json:"status,omitempty"`
}
// ClusterCondition describes current state of a cluster.
type ClusterCondition struct {
// Type of cluster condition, Ready or Offline.
Type common.ClusterConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status apiv1.ConditionStatus `json:"status"`
// Last time the condition was checked.
LastProbeTime metav1.Time `json:"lastProbeTime"`
// Last time the condition transit from one status to another.
// +optional
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
// (brief) reason for the condition's last transition.
// +optional
Reason *string `json:"reason,omitempty"`
// Human readable message indicating details about last transition.
// +optional
Message *string `json:"message,omitempty"`
}
// +kubebuilder:object:root=true
// KubeFedClusterList contains a list of KubeFedCluster
type KubeFedClusterList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []KubeFedCluster `json:"items"`
}
func init() {
SchemeBuilder.Register(&KubeFedCluster{}, &KubeFedClusterList{})
}

View File

@@ -0,0 +1,142 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1beta1
import (
apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// KubeFedConfigSpec defines the desired state of KubeFedConfig
type KubeFedConfigSpec struct {
// The scope of the KubeFed control plane should be either
// `Namespaced` or `Cluster`. `Namespaced` indicates that the
// KubeFed namespace will be the only target of the control plane.
Scope apiextv1b1.ResourceScope `json:"scope"`
// +optional
ControllerDuration *DurationConfig `json:"controllerDuration,omitempty"`
// +optional
LeaderElect *LeaderElectConfig `json:"leaderElect,omitempty"`
// +optional
FeatureGates []FeatureGatesConfig `json:"featureGates,omitempty"`
// +optional
ClusterHealthCheck *ClusterHealthCheckConfig `json:"clusterHealthCheck,omitempty"`
// +optional
SyncController *SyncControllerConfig `json:"syncController,omitempty"`
}
type DurationConfig struct {
// Time to wait before reconciling on a healthy cluster.
// +optional
AvailableDelay *metav1.Duration `json:"availableDelay,omitempty"`
// Time to wait before giving up on an unhealthy cluster.
// +optional
UnavailableDelay *metav1.Duration `json:"unavailableDelay,omitempty"`
}
type LeaderElectConfig struct {
// The duration that non-leader candidates will wait after observing a leadership
// renewal until attempting to acquire leadership of a led but unrenewed leader
// slot. This is effectively the maximum duration that a leader can be stopped
// before it is replaced by another candidate. This is only applicable if leader
// election is enabled.
// +optional
LeaseDuration *metav1.Duration `json:"leaseDuration,omitempty"`
// The interval between attempts by the acting master to renew a leadership slot
// before it stops leading. This must be less than or equal to the lease duration.
// This is only applicable if leader election is enabled.
// +optional
RenewDeadline *metav1.Duration `json:"renewDeadline,omitempty"`
// The duration the clients should wait between attempting acquisition and renewal
// of a leadership. This is only applicable if leader election is enabled.
// +optional
RetryPeriod *metav1.Duration `json:"retryPeriod,omitempty"`
// The type of resource object that is used for locking during
// leader election. Supported options are `configmaps` (default) and `endpoints`.
// +optional
ResourceLock *ResourceLockType `json:"resourceLock,omitempty"`
}
type ResourceLockType string
const (
ConfigMapsResourceLock ResourceLockType = "configmaps"
EndpointsResourceLock ResourceLockType = "endpoints"
)
type FeatureGatesConfig struct {
Name string `json:"name"`
Configuration ConfigurationMode `json:"configuration"`
}
type ConfigurationMode string
const (
ConfigurationEnabled ConfigurationMode = "Enabled"
ConfigurationDisabled ConfigurationMode = "Disabled"
)
type ClusterHealthCheckConfig struct {
// How often to monitor the cluster health.
// +optional
Period *metav1.Duration `json:"period,omitempty"`
// Minimum consecutive failures for the cluster health to be considered failed after having succeeded.
// +optional
FailureThreshold *int64 `json:"failureThreshold,omitempty"`
// Minimum consecutive successes for the cluster health to be considered successful after having failed.
// +optional
SuccessThreshold *int64 `json:"successThreshold,omitempty"`
// Duration after which the cluster health check times out.
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
}
type SyncControllerConfig struct {
// Whether to adopt pre-existing resources in member clusters. Defaults to
// "Enabled".
// +optional
AdoptResources *ResourceAdoption `json:"adoptResources,omitempty"`
}
type ResourceAdoption string
const (
AdoptResourcesEnabled ResourceAdoption = "Enabled"
AdoptResourcesDisabled ResourceAdoption = "Disabled"
)
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=kubefedconfigs
type KubeFedConfig struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KubeFedConfigSpec `json:"spec"`
}
// +kubebuilder:object:root=true
// KubeFedConfigList contains a list of KubeFedConfig
type KubeFedConfigList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []KubeFedConfig `json:"items"`
}
func init() {
SchemeBuilder.Register(&KubeFedConfig{}, &KubeFedConfigList{})
}

View File

@@ -0,0 +1,537 @@
// +build !ignore_autogenerated
/*
Copyright 2018 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 controller-gen. DO NOT EDIT.
package v1beta1
import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
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 *APIResource) DeepCopyInto(out *APIResource) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIResource.
func (in *APIResource) DeepCopy() *APIResource {
if in == nil {
return nil
}
out := new(APIResource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterCondition) DeepCopyInto(out *ClusterCondition) {
*out = *in
in.LastProbeTime.DeepCopyInto(&out.LastProbeTime)
if in.LastTransitionTime != nil {
in, out := &in.LastTransitionTime, &out.LastTransitionTime
*out = (*in).DeepCopy()
}
if in.Reason != nil {
in, out := &in.Reason, &out.Reason
*out = new(string)
**out = **in
}
if in.Message != nil {
in, out := &in.Message, &out.Message
*out = new(string)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCondition.
func (in *ClusterCondition) DeepCopy() *ClusterCondition {
if in == nil {
return nil
}
out := new(ClusterCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterHealthCheckConfig) DeepCopyInto(out *ClusterHealthCheckConfig) {
*out = *in
if in.Period != nil {
in, out := &in.Period, &out.Period
*out = new(v1.Duration)
**out = **in
}
if in.FailureThreshold != nil {
in, out := &in.FailureThreshold, &out.FailureThreshold
*out = new(int64)
**out = **in
}
if in.SuccessThreshold != nil {
in, out := &in.SuccessThreshold, &out.SuccessThreshold
*out = new(int64)
**out = **in
}
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(v1.Duration)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterHealthCheckConfig.
func (in *ClusterHealthCheckConfig) DeepCopy() *ClusterHealthCheckConfig {
if in == nil {
return nil
}
out := new(ClusterHealthCheckConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DurationConfig) DeepCopyInto(out *DurationConfig) {
*out = *in
if in.AvailableDelay != nil {
in, out := &in.AvailableDelay, &out.AvailableDelay
*out = new(v1.Duration)
**out = **in
}
if in.UnavailableDelay != nil {
in, out := &in.UnavailableDelay, &out.UnavailableDelay
*out = new(v1.Duration)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DurationConfig.
func (in *DurationConfig) DeepCopy() *DurationConfig {
if in == nil {
return nil
}
out := new(DurationConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FeatureGatesConfig) DeepCopyInto(out *FeatureGatesConfig) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureGatesConfig.
func (in *FeatureGatesConfig) DeepCopy() *FeatureGatesConfig {
if in == nil {
return nil
}
out := new(FeatureGatesConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedTypeConfig) DeepCopyInto(out *FederatedTypeConfig) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedTypeConfig.
func (in *FederatedTypeConfig) DeepCopy() *FederatedTypeConfig {
if in == nil {
return nil
}
out := new(FederatedTypeConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedTypeConfig) 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 *FederatedTypeConfigList) DeepCopyInto(out *FederatedTypeConfigList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]FederatedTypeConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedTypeConfigList.
func (in *FederatedTypeConfigList) DeepCopy() *FederatedTypeConfigList {
if in == nil {
return nil
}
out := new(FederatedTypeConfigList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FederatedTypeConfigList) 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 *FederatedTypeConfigSpec) DeepCopyInto(out *FederatedTypeConfigSpec) {
*out = *in
out.TargetType = in.TargetType
out.FederatedType = in.FederatedType
if in.StatusType != nil {
in, out := &in.StatusType, &out.StatusType
*out = new(APIResource)
**out = **in
}
if in.StatusCollection != nil {
in, out := &in.StatusCollection, &out.StatusCollection
*out = new(StatusCollectionMode)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedTypeConfigSpec.
func (in *FederatedTypeConfigSpec) DeepCopy() *FederatedTypeConfigSpec {
if in == nil {
return nil
}
out := new(FederatedTypeConfigSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederatedTypeConfigStatus) DeepCopyInto(out *FederatedTypeConfigStatus) {
*out = *in
if in.StatusController != nil {
in, out := &in.StatusController, &out.StatusController
*out = new(ControllerStatus)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FederatedTypeConfigStatus.
func (in *FederatedTypeConfigStatus) DeepCopy() *FederatedTypeConfigStatus {
if in == nil {
return nil
}
out := new(FederatedTypeConfigStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeFedCluster) DeepCopyInto(out *KubeFedCluster) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedCluster.
func (in *KubeFedCluster) DeepCopy() *KubeFedCluster {
if in == nil {
return nil
}
out := new(KubeFedCluster)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubeFedCluster) 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 *KubeFedClusterList) DeepCopyInto(out *KubeFedClusterList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]KubeFedCluster, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedClusterList.
func (in *KubeFedClusterList) DeepCopy() *KubeFedClusterList {
if in == nil {
return nil
}
out := new(KubeFedClusterList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubeFedClusterList) 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 *KubeFedClusterSpec) DeepCopyInto(out *KubeFedClusterSpec) {
*out = *in
if in.CABundle != nil {
in, out := &in.CABundle, &out.CABundle
*out = make([]byte, len(*in))
copy(*out, *in)
}
out.SecretRef = in.SecretRef
if in.DisabledTLSValidations != nil {
in, out := &in.DisabledTLSValidations, &out.DisabledTLSValidations
*out = make([]TLSValidation, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedClusterSpec.
func (in *KubeFedClusterSpec) DeepCopy() *KubeFedClusterSpec {
if in == nil {
return nil
}
out := new(KubeFedClusterSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeFedClusterStatus) DeepCopyInto(out *KubeFedClusterStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]ClusterCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Zones != nil {
in, out := &in.Zones, &out.Zones
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Region != nil {
in, out := &in.Region, &out.Region
*out = new(string)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedClusterStatus.
func (in *KubeFedClusterStatus) DeepCopy() *KubeFedClusterStatus {
if in == nil {
return nil
}
out := new(KubeFedClusterStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeFedConfig) DeepCopyInto(out *KubeFedConfig) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedConfig.
func (in *KubeFedConfig) DeepCopy() *KubeFedConfig {
if in == nil {
return nil
}
out := new(KubeFedConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubeFedConfig) 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 *KubeFedConfigList) DeepCopyInto(out *KubeFedConfigList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]KubeFedConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedConfigList.
func (in *KubeFedConfigList) DeepCopy() *KubeFedConfigList {
if in == nil {
return nil
}
out := new(KubeFedConfigList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubeFedConfigList) 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 *KubeFedConfigSpec) DeepCopyInto(out *KubeFedConfigSpec) {
*out = *in
if in.ControllerDuration != nil {
in, out := &in.ControllerDuration, &out.ControllerDuration
*out = new(DurationConfig)
(*in).DeepCopyInto(*out)
}
if in.LeaderElect != nil {
in, out := &in.LeaderElect, &out.LeaderElect
*out = new(LeaderElectConfig)
(*in).DeepCopyInto(*out)
}
if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates
*out = make([]FeatureGatesConfig, len(*in))
copy(*out, *in)
}
if in.ClusterHealthCheck != nil {
in, out := &in.ClusterHealthCheck, &out.ClusterHealthCheck
*out = new(ClusterHealthCheckConfig)
(*in).DeepCopyInto(*out)
}
if in.SyncController != nil {
in, out := &in.SyncController, &out.SyncController
*out = new(SyncControllerConfig)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeFedConfigSpec.
func (in *KubeFedConfigSpec) DeepCopy() *KubeFedConfigSpec {
if in == nil {
return nil
}
out := new(KubeFedConfigSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LeaderElectConfig) DeepCopyInto(out *LeaderElectConfig) {
*out = *in
if in.LeaseDuration != nil {
in, out := &in.LeaseDuration, &out.LeaseDuration
*out = new(v1.Duration)
**out = **in
}
if in.RenewDeadline != nil {
in, out := &in.RenewDeadline, &out.RenewDeadline
*out = new(v1.Duration)
**out = **in
}
if in.RetryPeriod != nil {
in, out := &in.RetryPeriod, &out.RetryPeriod
*out = new(v1.Duration)
**out = **in
}
if in.ResourceLock != nil {
in, out := &in.ResourceLock, &out.ResourceLock
*out = new(ResourceLockType)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeaderElectConfig.
func (in *LeaderElectConfig) DeepCopy() *LeaderElectConfig {
if in == nil {
return nil
}
out := new(LeaderElectConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LocalSecretReference) DeepCopyInto(out *LocalSecretReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalSecretReference.
func (in *LocalSecretReference) DeepCopy() *LocalSecretReference {
if in == nil {
return nil
}
out := new(LocalSecretReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SyncControllerConfig) DeepCopyInto(out *SyncControllerConfig) {
*out = *in
if in.AdoptResources != nil {
in, out := &in.AdoptResources, &out.AdoptResources
*out = new(ResourceAdoption)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SyncControllerConfig.
func (in *SyncControllerConfig) DeepCopy() *SyncControllerConfig {
if in == nil {
return nil
}
out := new(SyncControllerConfig)
in.DeepCopyInto(out)
return out
}

View File

@@ -0,0 +1,85 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Targets is a representation of a list of targets for an endpoint.
type Targets []string
// TTL is a structure defining the TTL of a DNS record
type TTL int64
// Labels store metadata related to the endpoint
// it is then stored in a persistent storage via serialization
type Labels map[string]string
// Endpoint is a high-level association between a service and an IP.
type Endpoint struct {
// The FQDN of the DNS record.
DNSName string `json:"dnsName,omitempty"`
// The targets that the DNS record points to.
Targets Targets `json:"targets,omitempty"`
// RecordType type of record, e.g. CNAME, A, SRV, TXT etc.
RecordType string `json:"recordType,omitempty"`
// TTL for the record in seconds.
RecordTTL TTL `json:"recordTTL,omitempty"`
// Labels stores labels defined for the Endpoint.
// +optional
Labels Labels `json:"labels,omitempty"`
}
// DNSEndpointSpec defines the desired state of DNSEndpoint
type DNSEndpointSpec struct {
Endpoints []*Endpoint `json:"endpoints,omitempty"`
}
// DNSEndpointStatus defines the observed state of DNSEndpoint
type DNSEndpointStatus struct {
// ObservedGeneration is the generation as observed by the controller consuming the DNSEndpoint.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=dnsendpoints
// +kubebuilder:subresource:status
// DNSEndpoint is the CRD wrapper for Endpoint which is designed to act as a
// source of truth for external-dns.
type DNSEndpoint struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DNSEndpointSpec `json:"spec,omitempty"`
Status DNSEndpointStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// DNSEndpointList contains a list of DNSEndpoint
type DNSEndpointList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []DNSEndpoint `json:"items"`
}
func init() {
SchemeBuilder.Register(&DNSEndpoint{}, &DNSEndpointList{})
}

View File

@@ -0,0 +1,47 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=domains
type Domain struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Domain is the DNS zone associated with the KubeFed control plane
Domain string `json:"domain"`
// NameServer is the authoritative DNS name server for the KubeFed domain
NameServer string `json:"nameServer,omitempty"`
}
// +kubebuilder:object:root=true
// DomainList contains a list of Domain
type DomainList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Domain `json:"items"`
}
func init() {
SchemeBuilder.Register(&Domain{}, &DomainList{})
}

View File

@@ -0,0 +1,43 @@
/*
Copyright 2019 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.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1alpha1 contains API Schema definitions for the multiclusterdns v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=multiclusterdns.kubefed.io
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "multiclusterdns.kubefed.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme is required by pkg/client/...
AddToScheme = SchemeBuilder.AddToScheme
)
// Resource is required by pkg/client/listers/...
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

View File

@@ -0,0 +1,69 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// IngressDNSRecordSpec defines the desired state of IngressDNSRecord
type IngressDNSRecordSpec struct {
// Host from the IngressRule in Cluster Ingress Spec
Hosts []string `json:"hosts,omitempty"`
// RecordTTL is the TTL in seconds for DNS records created for the Ingress, if omitted a default would be used
RecordTTL TTL `json:"recordTTL,omitempty"`
}
// IngressDNSRecordStatus defines the observed state of IngressDNSRecord
type IngressDNSRecordStatus struct {
// Array of Ingress Controller LoadBalancers
DNS []ClusterIngressDNS `json:"dns,omitempty"`
}
// ClusterIngressDNS defines the observed status of Ingress within a cluster.
type ClusterIngressDNS struct {
// Cluster name
Cluster string `json:"cluster,omitempty"`
// LoadBalancer for the corresponding ingress controller
LoadBalancer corev1.LoadBalancerStatus `json:"loadBalancer,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=ingressdnsrecords
// +kubebuilder:subresource:status
type IngressDNSRecord struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec IngressDNSRecordSpec `json:"spec,omitempty"`
Status IngressDNSRecordStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// IngressDNSRecordList contains a list of IngressDNSRecord
type IngressDNSRecordList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []IngressDNSRecord `json:"items"`
}
func init() {
SchemeBuilder.Register(&IngressDNSRecord{}, &IngressDNSRecordList{})
}

View File

@@ -0,0 +1,107 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ServiceDNSRecordSpec defines the desired state of ServiceDNSRecord.
type ServiceDNSRecordSpec struct {
// DomainRef is the name of the domain object to which the corresponding federated service belongs
DomainRef string `json:"domainRef"`
// RecordTTL is the TTL in seconds for DNS records created for this Service, if omitted a default would be used
RecordTTL TTL `json:"recordTTL,omitempty"`
// DNSPrefix when specified, an additional DNS record would be created with <DNSPrefix>.<KubeFedDomain>
DNSPrefix string `json:"dnsPrefix,omitempty"`
// ExternalName when specified, replaces the service name portion of a resource record
// with the value of ExternalName.
ExternalName string `json:"externalName,omitempty"`
// AllowServiceWithoutEndpoints allows DNS records to be written for Service shards without endpoints
AllowServiceWithoutEndpoints bool `json:"allowServiceWithoutEndpoints,omitempty"`
}
// ServiceDNSRecordStatus defines the observed state of ServiceDNSRecord.
type ServiceDNSRecordStatus struct {
// Domain is the DNS domain of the KubeFed control plane as in Domain API
Domain string `json:"domain,omitempty"`
DNS []ClusterDNS `json:"dns,omitempty"`
}
// ClusterDNS defines the observed status of LoadBalancer within a cluster.
type ClusterDNS struct {
// Cluster name
Cluster string `json:"cluster,omitempty"`
// LoadBalancer for the corresponding service
LoadBalancer corev1.LoadBalancerStatus `json:"loadBalancer,omitempty"`
// Zones to which the cluster belongs
Zones []string `json:"zones,omitempty"`
// Region to which the cluster belongs
Region string `json:"region,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServiceDNSRecord defines a scheme of DNS name and subdomains that
// should be programmed with endpoint information about a Service deployed in
// multiple Kubernetes clusters. ServiceDNSRecord is name-associated
// with the Services it programs endpoint information for, meaning that a
// ServiceDNSRecord expresses the intent to program DNS with
// information about endpoints for the Kubernetes Service resources with the
// same name and namespace in different clusters.
//
// For the example, given the following values:
//
// metadata.name: test-service
// metadata.namespace: test-namespace
// spec.federationName: test-federation
//
// the following set of DNS names will be programmed:
//
// Global Level: test-service.test-namespace.test-federation.svc.<federation-domain>
// Region Level: test-service.test-namespace.test-federation.svc.(status.DNS[*].region).<federation-domain>
// Zone Level : test-service.test-namespace.test-federation.svc.(status.DNS[*].zone).(status.DNS[*].region).<federation-domain>
//
// Optionally, when DNSPrefix is specified, another DNS name will be programmed
// which would be a CNAME record pointing to DNS name at global level as below:
// <dns-prefix>.<federation-domain> --> test-service.test-namespace.test-federation.svc.<federation-domain>
//
// +k8s:openapi-gen=true
// +kubebuilder:resource:path=servicednsrecords
// +kubebuilder:subresource:status
type ServiceDNSRecord struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ServiceDNSRecordSpec `json:"spec,omitempty"`
Status ServiceDNSRecordStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServiceDNSRecordList contains a list of ServiceDNSRecord
type ServiceDNSRecordList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ServiceDNSRecord `json:"items"`
}
func init() {
SchemeBuilder.Register(&ServiceDNSRecord{}, &ServiceDNSRecordList{})
}

View File

@@ -0,0 +1,483 @@
// +build !ignore_autogenerated
/*
Copyright 2018 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 controller-gen. DO NOT EDIT.
package v1alpha1
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 *ClusterDNS) DeepCopyInto(out *ClusterDNS) {
*out = *in
in.LoadBalancer.DeepCopyInto(&out.LoadBalancer)
if in.Zones != nil {
in, out := &in.Zones, &out.Zones
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDNS.
func (in *ClusterDNS) DeepCopy() *ClusterDNS {
if in == nil {
return nil
}
out := new(ClusterDNS)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterIngressDNS) DeepCopyInto(out *ClusterIngressDNS) {
*out = *in
in.LoadBalancer.DeepCopyInto(&out.LoadBalancer)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngressDNS.
func (in *ClusterIngressDNS) DeepCopy() *ClusterIngressDNS {
if in == nil {
return nil
}
out := new(ClusterIngressDNS)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DNSEndpoint) DeepCopyInto(out *DNSEndpoint) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpoint.
func (in *DNSEndpoint) DeepCopy() *DNSEndpoint {
if in == nil {
return nil
}
out := new(DNSEndpoint)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DNSEndpoint) 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 *DNSEndpointList) DeepCopyInto(out *DNSEndpointList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]DNSEndpoint, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointList.
func (in *DNSEndpointList) DeepCopy() *DNSEndpointList {
if in == nil {
return nil
}
out := new(DNSEndpointList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DNSEndpointList) 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 *DNSEndpointSpec) DeepCopyInto(out *DNSEndpointSpec) {
*out = *in
if in.Endpoints != nil {
in, out := &in.Endpoints, &out.Endpoints
*out = make([]*Endpoint, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Endpoint)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointSpec.
func (in *DNSEndpointSpec) DeepCopy() *DNSEndpointSpec {
if in == nil {
return nil
}
out := new(DNSEndpointSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DNSEndpointStatus) DeepCopyInto(out *DNSEndpointStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointStatus.
func (in *DNSEndpointStatus) DeepCopy() *DNSEndpointStatus {
if in == nil {
return nil
}
out := new(DNSEndpointStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Domain) DeepCopyInto(out *Domain) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Domain.
func (in *Domain) DeepCopy() *Domain {
if in == nil {
return nil
}
out := new(Domain)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Domain) 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 *DomainList) DeepCopyInto(out *DomainList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Domain, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainList.
func (in *DomainList) DeepCopy() *DomainList {
if in == nil {
return nil
}
out := new(DomainList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DomainList) 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 *Endpoint) DeepCopyInto(out *Endpoint) {
*out = *in
if in.Targets != nil {
in, out := &in.Targets, &out.Targets
*out = make(Targets, len(*in))
copy(*out, *in)
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(Labels, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint.
func (in *Endpoint) DeepCopy() *Endpoint {
if in == nil {
return nil
}
out := new(Endpoint)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressDNSRecord) DeepCopyInto(out *IngressDNSRecord) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressDNSRecord.
func (in *IngressDNSRecord) DeepCopy() *IngressDNSRecord {
if in == nil {
return nil
}
out := new(IngressDNSRecord)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IngressDNSRecord) 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 *IngressDNSRecordList) DeepCopyInto(out *IngressDNSRecordList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]IngressDNSRecord, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressDNSRecordList.
func (in *IngressDNSRecordList) DeepCopy() *IngressDNSRecordList {
if in == nil {
return nil
}
out := new(IngressDNSRecordList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IngressDNSRecordList) 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 *IngressDNSRecordSpec) DeepCopyInto(out *IngressDNSRecordSpec) {
*out = *in
if in.Hosts != nil {
in, out := &in.Hosts, &out.Hosts
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressDNSRecordSpec.
func (in *IngressDNSRecordSpec) DeepCopy() *IngressDNSRecordSpec {
if in == nil {
return nil
}
out := new(IngressDNSRecordSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressDNSRecordStatus) DeepCopyInto(out *IngressDNSRecordStatus) {
*out = *in
if in.DNS != nil {
in, out := &in.DNS, &out.DNS
*out = make([]ClusterIngressDNS, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressDNSRecordStatus.
func (in *IngressDNSRecordStatus) DeepCopy() *IngressDNSRecordStatus {
if in == nil {
return nil
}
out := new(IngressDNSRecordStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Labels) DeepCopyInto(out *Labels) {
{
in := &in
*out = make(Labels, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Labels.
func (in Labels) DeepCopy() Labels {
if in == nil {
return nil
}
out := new(Labels)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceDNSRecord) DeepCopyInto(out *ServiceDNSRecord) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDNSRecord.
func (in *ServiceDNSRecord) DeepCopy() *ServiceDNSRecord {
if in == nil {
return nil
}
out := new(ServiceDNSRecord)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ServiceDNSRecord) 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 *ServiceDNSRecordList) DeepCopyInto(out *ServiceDNSRecordList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ServiceDNSRecord, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDNSRecordList.
func (in *ServiceDNSRecordList) DeepCopy() *ServiceDNSRecordList {
if in == nil {
return nil
}
out := new(ServiceDNSRecordList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ServiceDNSRecordList) 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 *ServiceDNSRecordSpec) DeepCopyInto(out *ServiceDNSRecordSpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDNSRecordSpec.
func (in *ServiceDNSRecordSpec) DeepCopy() *ServiceDNSRecordSpec {
if in == nil {
return nil
}
out := new(ServiceDNSRecordSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceDNSRecordStatus) DeepCopyInto(out *ServiceDNSRecordStatus) {
*out = *in
if in.DNS != nil {
in, out := &in.DNS, &out.DNS
*out = make([]ClusterDNS, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceDNSRecordStatus.
func (in *ServiceDNSRecordStatus) DeepCopy() *ServiceDNSRecordStatus {
if in == nil {
return nil
}
out := new(ServiceDNSRecordStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Targets) DeepCopyInto(out *Targets) {
{
in := &in
*out = make(Targets, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Targets.
func (in Targets) DeepCopy() Targets {
if in == nil {
return nil
}
out := new(Targets)
in.DeepCopyInto(out)
return *out
}

View File

@@ -0,0 +1,43 @@
/*
Copyright 2019 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.
*/
// NOTE: Boilerplate only. Ignore this file.
// Package v1alpha1 contains API Schema definitions for the scheduling v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=scheduling.kubefed.io
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: "scheduling.kubefed.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}
// AddToScheme is required by pkg/client/...
AddToScheme = SchemeBuilder.AddToScheme
)
// Resource is required by pkg/client/listers/...
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

View File

@@ -0,0 +1,99 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ReplicaSchedulingPreferenceSpec defines the desired state of ReplicaSchedulingPreference
type ReplicaSchedulingPreferenceSpec struct {
//TODO (@irfanurrehman); upgrade this to label selector only if need be.
// The idea of this API is to have a a set of preferences which can
// be used for a target FederatedDeployment or FederatedReplicaset.
// Although the set of preferences in question can be applied to multiple
// target objects using label selectors, but there are no clear advantages
// of doing that as of now.
// To keep the implementation and usage simple, matching ns/name of RSP
// resource to the target resource is sufficient and only additional information
// needed in RSP resource is a target kind (FederatedDeployment or FederatedReplicaset).
TargetKind string `json:"targetKind"`
// Total number of pods desired across federated clusters.
// Replicas specified in the spec for target deployment template or replicaset
// template will be discarded/overridden when scheduling preferences are
// specified.
TotalReplicas int32 `json:"totalReplicas"`
// If set to true then already scheduled and running replicas may be moved to other clusters
// in order to match current state to the specified preferences. Otherwise, if set to false,
// up and running replicas will not be moved.
// +optional
Rebalance bool `json:"rebalance,omitempty"`
// A mapping between cluster names and preferences regarding a local workload object (dep, rs, .. ) in
// these clusters.
// "*" (if provided) applies to all clusters if an explicit mapping is not provided.
// If omitted, clusters without explicit preferences should not have any replicas scheduled.
// +optional
Clusters map[string]ClusterPreferences `json:"clusters,omitempty"`
}
// Preferences regarding number of replicas assigned to a cluster workload object (dep, rs, ..) within
// a federated workload object.
type ClusterPreferences struct {
// Minimum number of replicas that should be assigned to this cluster workload object. 0 by default.
// +optional
MinReplicas int64 `json:"minReplicas,omitempty"`
// Maximum number of replicas that should be assigned to this cluster workload object.
// Unbounded if no value provided (default).
// +optional
MaxReplicas *int64 `json:"maxReplicas,omitempty"`
// A number expressing the preference to put an additional replica to this cluster workload object.
// 0 by default.
Weight int64 `json:"weight,omitempty"`
}
// ReplicaSchedulingPreferenceStatus defines the observed state of ReplicaSchedulingPreference
type ReplicaSchedulingPreferenceStatus struct {
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=replicaschedulingpreferences
type ReplicaSchedulingPreference struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ReplicaSchedulingPreferenceSpec `json:"spec,omitempty"`
Status ReplicaSchedulingPreferenceStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// ReplicaSchedulingPreferenceList contains a list of ReplicaSchedulingPreference
type ReplicaSchedulingPreferenceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ReplicaSchedulingPreference `json:"items"`
}
func init() {
SchemeBuilder.Register(&ReplicaSchedulingPreference{}, &ReplicaSchedulingPreferenceList{})
}

View File

@@ -0,0 +1,141 @@
// +build !ignore_autogenerated
/*
Copyright 2018 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 controller-gen. DO NOT EDIT.
package v1alpha1
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 *ClusterPreferences) DeepCopyInto(out *ClusterPreferences) {
*out = *in
if in.MaxReplicas != nil {
in, out := &in.MaxReplicas, &out.MaxReplicas
*out = new(int64)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterPreferences.
func (in *ClusterPreferences) DeepCopy() *ClusterPreferences {
if in == nil {
return nil
}
out := new(ClusterPreferences)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplicaSchedulingPreference) DeepCopyInto(out *ReplicaSchedulingPreference) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplicaSchedulingPreference.
func (in *ReplicaSchedulingPreference) DeepCopy() *ReplicaSchedulingPreference {
if in == nil {
return nil
}
out := new(ReplicaSchedulingPreference)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ReplicaSchedulingPreference) 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 *ReplicaSchedulingPreferenceList) DeepCopyInto(out *ReplicaSchedulingPreferenceList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ReplicaSchedulingPreference, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplicaSchedulingPreferenceList.
func (in *ReplicaSchedulingPreferenceList) DeepCopy() *ReplicaSchedulingPreferenceList {
if in == nil {
return nil
}
out := new(ReplicaSchedulingPreferenceList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ReplicaSchedulingPreferenceList) 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 *ReplicaSchedulingPreferenceSpec) DeepCopyInto(out *ReplicaSchedulingPreferenceSpec) {
*out = *in
if in.Clusters != nil {
in, out := &in.Clusters, &out.Clusters
*out = make(map[string]ClusterPreferences, len(*in))
for key, val := range *in {
(*out)[key] = *val.DeepCopy()
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplicaSchedulingPreferenceSpec.
func (in *ReplicaSchedulingPreferenceSpec) DeepCopy() *ReplicaSchedulingPreferenceSpec {
if in == nil {
return nil
}
out := new(ReplicaSchedulingPreferenceSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplicaSchedulingPreferenceStatus) DeepCopyInto(out *ReplicaSchedulingPreferenceStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplicaSchedulingPreferenceStatus.
func (in *ReplicaSchedulingPreferenceStatus) DeepCopy() *ReplicaSchedulingPreferenceStatus {
if in == nil {
return nil
}
out := new(ReplicaSchedulingPreferenceStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -0,0 +1,91 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generic
import (
"context"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/kubefed/pkg/client/generic/scheme"
)
type Client interface {
Create(ctx context.Context, obj runtime.Object) error
Get(ctx context.Context, obj runtime.Object, namespace, name string) error
Update(ctx context.Context, obj runtime.Object) error
Delete(ctx context.Context, obj runtime.Object, namespace, name string) error
List(ctx context.Context, obj runtime.Object, namespace string, opts ...client.ListOption) error
UpdateStatus(ctx context.Context, obj runtime.Object) error
}
type genericClient struct {
client client.Client
}
func New(config *rest.Config) (Client, error) {
client, err := client.New(config, client.Options{Scheme: scheme.Scheme})
return &genericClient{client}, err
}
func NewForConfigOrDie(config *rest.Config) Client {
client, err := New(config)
if err != nil {
panic(err)
}
return client
}
func NewForConfigOrDieWithUserAgent(config *rest.Config, userAgent string) Client {
configCopy := rest.CopyConfig(config)
rest.AddUserAgent(configCopy, userAgent)
return NewForConfigOrDie(configCopy)
}
func (c *genericClient) Create(ctx context.Context, obj runtime.Object) error {
return c.client.Create(ctx, obj)
}
func (c *genericClient) Get(ctx context.Context, obj runtime.Object, namespace, name string) error {
return c.client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, obj)
}
func (c *genericClient) Update(ctx context.Context, obj runtime.Object) error {
return c.client.Update(ctx, obj)
}
func (c *genericClient) Delete(ctx context.Context, obj runtime.Object, namespace, name string) error {
accessor, err := meta.Accessor(obj)
if err != nil {
return err
}
accessor.SetNamespace(namespace)
accessor.SetName(name)
return c.client.Delete(ctx, obj)
}
func (c *genericClient) List(ctx context.Context, obj runtime.Object, namespace string, opts ...client.ListOption) error {
opts = append(opts, client.InNamespace(namespace))
return c.client.List(ctx, obj, opts...)
}
func (c *genericClient) UpdateStatus(ctx context.Context, obj runtime.Object) error {
return c.client.Status().Update(ctx, obj)
}

View File

@@ -0,0 +1,43 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package scheme
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
schema "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
k8sscheme "k8s.io/client-go/kubernetes/scheme"
fedapis "sigs.k8s.io/kubefed/pkg/apis"
)
var Scheme = runtime.NewScheme()
var Codecs = serializer.NewCodecFactory(Scheme)
var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
fedapis.AddToScheme,
k8sscheme.AddToScheme,
}
func init() {
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
utilruntime.Must(AddToScheme(Scheme))
}
var AddToScheme = localSchemeBuilder.AddToScheme

View File

@@ -0,0 +1,36 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"time"
"k8s.io/client-go/util/flowcontrol"
)
func StartBackoffGC(backoff *flowcontrol.Backoff, stopCh <-chan struct{}) {
go func() {
for {
select {
case <-time.After(time.Minute):
backoff.GC()
case <-stopCh:
return
}
}
}()
}

View File

@@ -0,0 +1,221 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"context"
"crypto/tls"
"crypto/x509"
"net"
"net/http"
"net/url"
"time"
"github.com/pkg/errors"
apiv1 "k8s.io/api/core/v1"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/transport"
"k8s.io/klog"
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
"sigs.k8s.io/kubefed/pkg/client/generic"
)
const (
DefaultKubeFedSystemNamespace = "kube-federation-system"
KubeAPIQPS = 20.0
KubeAPIBurst = 30
TokenKey = "token"
KubeFedConfigName = "kubefed"
)
// BuildClusterConfig returns a restclient.Config that can be used to configure
// a client for the given KubeFedCluster or an error. The client is used to
// access kubernetes secrets in the kubefed namespace.
func BuildClusterConfig(fedCluster *fedv1b1.KubeFedCluster, client generic.Client, fedNamespace string) (*restclient.Config, error) {
clusterName := fedCluster.Name
apiEndpoint := fedCluster.Spec.APIEndpoint
// TODO(marun) Remove when validation ensures a non-empty value.
if apiEndpoint == "" {
return nil, errors.Errorf("The api endpoint of cluster %s is empty", clusterName)
}
secretName := fedCluster.Spec.SecretRef.Name
if secretName == "" {
return nil, errors.Errorf("Cluster %s does not have a secret name", clusterName)
}
secret := &apiv1.Secret{}
err := client.Get(context.TODO(), secret, fedNamespace, secretName)
if err != nil {
return nil, err
}
token, tokenFound := secret.Data[TokenKey]
if !tokenFound || len(token) == 0 {
return nil, errors.Errorf("The secret for cluster %s is missing a non-empty value for %q", clusterName, TokenKey)
}
clusterConfig, err := clientcmd.BuildConfigFromFlags(apiEndpoint, "")
if err != nil {
return nil, err
}
clusterConfig.CAData = fedCluster.Spec.CABundle
clusterConfig.BearerToken = string(token)
clusterConfig.QPS = KubeAPIQPS
clusterConfig.Burst = KubeAPIBurst
if len(fedCluster.Spec.DisabledTLSValidations) != 0 {
klog.V(1).Infof("Cluster %s will use a custom transport for TLS certificate validation", fedCluster.Name)
if err = CustomizeTLSTransport(fedCluster, clusterConfig); err != nil {
return nil, err
}
}
return clusterConfig, nil
}
// IsPrimaryCluster checks if the caller is working with objects for the
// primary cluster by checking if the UIDs match for both ObjectMetas passed
// in.
// TODO (font): Need to revisit this when cluster ID is available.
func IsPrimaryCluster(obj, clusterObj pkgruntime.Object) bool {
meta := MetaAccessor(obj)
clusterMeta := MetaAccessor(clusterObj)
return meta.GetUID() == clusterMeta.GetUID()
}
// CustomizeTLSTransport replaces the restclient.Config.Transport with one that
// implements the desired TLS certificate validations
func CustomizeTLSTransport(fedCluster *fedv1b1.KubeFedCluster, clientConfig *restclient.Config) error {
clientTransportConfig, err := clientConfig.TransportConfig()
if err != nil {
return errors.Errorf("Cluster %s client transport config error: %s", fedCluster.Name, err)
}
transportConfig, err := transport.TLSConfigFor(clientTransportConfig)
if err != nil {
return errors.Errorf("Cluster %s transport error: %s", fedCluster.Name, err)
}
err = CustomizeCertificateValidation(fedCluster, transportConfig)
if err != nil {
return errors.Errorf("Cluster %s custom certificate validation error: %s", fedCluster.Name, err)
}
// using the same defaults as http.DefaultTransport
clientConfig.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: transportConfig,
}
clientConfig.TLSClientConfig = restclient.TLSClientConfig{}
return nil
}
// CustomizeCertificateValidation modifies an existing tls.Config to disable the
// desired TLS checks in KubeFedCluster config
func CustomizeCertificateValidation(fedCluster *fedv1b1.KubeFedCluster, tlsConfig *tls.Config) error {
// InsecureSkipVerify must be enabled to prevent early validation errors from
// returning before VerifyPeerCertificate is run
tlsConfig.InsecureSkipVerify = true
var ignoreSubjectName, ignoreValidityPeriod bool
for _, validation := range fedCluster.Spec.DisabledTLSValidations {
switch fedv1b1.TLSValidation(validation) {
case fedv1b1.TLSAll:
klog.V(1).Infof("Cluster %s will not perform TLS certificate validation", fedCluster.Name)
return nil
case fedv1b1.TLSSubjectName:
ignoreSubjectName = true
case fedv1b1.TLSValidityPeriod:
ignoreValidityPeriod = true
}
}
// Normal TLS SubjectName validation uses the conn dnsname for validation,
// but this is not available when using a VerifyPeerCertificate functions.
// As a workaround, we will fill the tls.Config.ServerName with the URL host
// specified as the KubeFedCluster API target
if !ignoreSubjectName && tlsConfig.ServerName == "" {
apiURL, err := url.Parse(fedCluster.Spec.APIEndpoint)
if err != nil {
return errors.Errorf("failed to identify a valid host from APIEndpoint for use in SubjectName validation")
}
tlsConfig.ServerName = apiURL.Hostname()
}
// VerifyPeerCertificate uses the same logic as crypto/tls Conn.verifyServerCertificate
// but uses a modified set of options to ignore specific validations
tlsConfig.VerifyPeerCertificate = func(certificates [][]byte, verifiedChains [][]*x509.Certificate) error {
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
Intermediates: x509.NewCertPool(),
DNSName: tlsConfig.ServerName,
}
if tlsConfig.Time != nil {
opts.CurrentTime = tlsConfig.Time()
}
certs := make([]*x509.Certificate, len(certificates))
for i, asn1Data := range certificates {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return errors.New("tls: failed to parse certificate from server: " + err.Error())
}
certs[i] = cert
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
if ignoreSubjectName {
// set the DNSName to nil to ignore the name validation
opts.DNSName = ""
klog.V(1).Infof("Cluster %s will not perform tls certificate SubjectName validation", fedCluster.Name)
}
if ignoreValidityPeriod {
// set the CurrentTime to immediately after the certificate start time
// this will ensure that certificate passes the validity period check
opts.CurrentTime = certs[0].NotBefore.Add(time.Second)
klog.V(1).Infof("Cluster %s will not perform tls certificate ValidityPeriod validation", fedCluster.Name)
}
_, err := certs[0].Verify(opts)
return err
}
return nil
}

View File

@@ -0,0 +1,76 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"time"
)
// Providing 0 duration to an informer indicates that resync should be delayed as long as possible
const (
NoResyncPeriod time.Duration = 0 * time.Second
NamespaceName = "namespaces"
NamespaceKind = "Namespace"
ServiceKind = "Service"
ServiceAccountKind = "ServiceAccount"
// The following fields are used to interact with unstructured
// resources.
// Common fields
SpecField = "spec"
StatusField = "status"
MetadataField = "metadata"
// ServiceAccount fields
SecretsField = "secrets"
// Scale types
ReplicasField = "replicas"
RetainReplicasField = "retainReplicas"
// Template fields
TemplateField = "template"
// Placement fields
PlacementField = "placement"
ClusterSelectorField = "clusterSelector"
MatchLabelsField = "matchLabels"
// Override fields
OverridesField = "overrides"
ClusterNameField = "clusterName"
ClusterOverridesField = "clusterOverrides"
PathField = "path"
ValueField = "value"
// Cluster reference
ClustersField = "clusters"
NameField = "name"
)
type ReconciliationStatus int
const (
StatusAllOK ReconciliationStatus = iota
StatusNeedsRecheck
StatusError
StatusNotSynced
)

View File

@@ -0,0 +1,80 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
restclient "k8s.io/client-go/rest"
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
)
// LeaderElectionConfiguration defines the configuration of leader election
// clients for controller that can run with leader election enabled.
type LeaderElectionConfiguration struct {
// leaseDuration is the duration that non-leader candidates will wait
// after observing a leadership renewal until attempting to acquire
// leadership of a led but unrenewed leader slot. This is effectively the
// maximum duration that a leader can be stopped before it is replaced
// by another candidate. This is only applicable if leader election is
// enabled.
LeaseDuration time.Duration
// renewDeadline is the interval between attempts by the acting master to
// renew a leadership slot before it stops leading. This must be less
// than or equal to the lease duration. This is only applicable if leader
// election is enabled.
RenewDeadline time.Duration
// retryPeriod is the duration the clients should wait between attempting
// acquisition and renewal of a leadership. This is only applicable if
// leader election is enabled.
RetryPeriod time.Duration
// resourceLock indicates the resource object type that will be used to lock
// during leader election cycles.
ResourceLock fedv1b1.ResourceLockType
}
// KubeFedNamespaces defines the namespace configuration shared by
// most kubefed controllers.
type KubeFedNamespaces struct {
KubeFedNamespace string
TargetNamespace string
}
// ClusterHealthCheckConfig defines the configurable parameters for cluster health check
type ClusterHealthCheckConfig struct {
Period time.Duration
FailureThreshold int64
SuccessThreshold int64
Timeout time.Duration
}
// ControllerConfig defines the configuration common to KubeFed
// controllers.
type ControllerConfig struct {
KubeFedNamespaces
KubeConfig *restclient.Config
ClusterAvailableDelay time.Duration
ClusterUnavailableDelay time.Duration
MinimizeLatency bool
SkipAdoptingResources bool
}
func (c *ControllerConfig) LimitedScope() bool {
return c.KubeFedNamespaces.TargetNamespace != metav1.NamespaceAll
}

View File

@@ -0,0 +1,183 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// TODO: consider moving it to a more generic package.
package util
import (
"container/heap"
"time"
)
const (
// TODO: Investigate what capacity is right.
delayingDelivererUpdateChanCapacity = 1000
)
// DelayingDelivererItem is structure delivered by DelayingDeliverer to the
// target channel.
type DelayingDelivererItem struct {
// Key under which the value was added to deliverer.
Key string
// Value of the item.
Value interface{}
// When the item should be delivered.
DeliveryTime time.Time
}
type delivererHeap struct {
keyPosition map[string]int
data []*DelayingDelivererItem
}
// Functions required by container.Heap.
func (dh *delivererHeap) Len() int { return len(dh.data) }
func (dh *delivererHeap) Less(i, j int) bool {
return dh.data[i].DeliveryTime.Before(dh.data[j].DeliveryTime)
}
func (dh *delivererHeap) Swap(i, j int) {
dh.keyPosition[dh.data[i].Key] = j
dh.keyPosition[dh.data[j].Key] = i
dh.data[i], dh.data[j] = dh.data[j], dh.data[i]
}
func (dh *delivererHeap) Push(x interface{}) {
item := x.(*DelayingDelivererItem)
dh.data = append(dh.data, item)
dh.keyPosition[item.Key] = len(dh.data) - 1
}
func (dh *delivererHeap) Pop() interface{} {
n := len(dh.data)
item := dh.data[n-1]
dh.data = dh.data[:n-1]
delete(dh.keyPosition, item.Key)
return item
}
// A structure that pushes the items to the target channel at a given time.
type DelayingDeliverer struct {
// Channel to deliver the data when their time comes.
targetChannel chan *DelayingDelivererItem
// Store for data
heap *delivererHeap
// Channel to feed the main goroutine with updates.
updateChannel chan *DelayingDelivererItem
// To stop the main goroutine.
stopChannel chan struct{}
}
func NewDelayingDeliverer() *DelayingDeliverer {
return NewDelayingDelivererWithChannel(make(chan *DelayingDelivererItem, 100))
}
func NewDelayingDelivererWithChannel(targetChannel chan *DelayingDelivererItem) *DelayingDeliverer {
return &DelayingDeliverer{
targetChannel: targetChannel,
heap: &delivererHeap{
keyPosition: make(map[string]int),
data: make([]*DelayingDelivererItem, 0),
},
updateChannel: make(chan *DelayingDelivererItem, delayingDelivererUpdateChanCapacity),
stopChannel: make(chan struct{}),
}
}
// Deliver all items due before or equal to timestamp.
func (d *DelayingDeliverer) deliver(timestamp time.Time) {
for d.heap.Len() > 0 {
if timestamp.Before(d.heap.data[0].DeliveryTime) {
return
}
item := heap.Pop(d.heap).(*DelayingDelivererItem)
d.targetChannel <- item
}
}
func (d *DelayingDeliverer) run() {
for {
now := time.Now()
d.deliver(now)
nextWakeUp := now.Add(time.Hour)
if d.heap.Len() > 0 {
nextWakeUp = d.heap.data[0].DeliveryTime
}
sleepTime := nextWakeUp.Sub(now)
select {
case <-time.After(sleepTime):
break // just wake up and process the data
case item := <-d.updateChannel:
if position, found := d.heap.keyPosition[item.Key]; found {
if item.DeliveryTime.Before(d.heap.data[position].DeliveryTime) {
d.heap.data[position] = item
heap.Fix(d.heap, position)
}
// Ignore if later.
} else {
heap.Push(d.heap, item)
}
case <-d.stopChannel:
return
}
}
}
// Starts the DelayingDeliverer.
func (d *DelayingDeliverer) Start() {
go d.run()
}
// Stops the DelayingDeliverer. Undelivered items are discarded.
func (d *DelayingDeliverer) Stop() {
close(d.stopChannel)
}
// Delivers value at the given time.
func (d *DelayingDeliverer) DeliverAt(key string, value interface{}, deliveryTime time.Time) {
d.updateChannel <- &DelayingDelivererItem{
Key: key,
Value: value,
DeliveryTime: deliveryTime,
}
}
// Delivers value after the given delay.
func (d *DelayingDeliverer) DeliverAfter(key string, value interface{}, delay time.Duration) {
d.DeliverAt(key, value, time.Now().Add(delay))
}
// Gets target channel of the deliverer.
func (d *DelayingDeliverer) GetTargetChannel() chan *DelayingDelivererItem {
return d.targetChannel
}
// Starts Delaying deliverer with a handler listening on the target channel.
func (d *DelayingDeliverer) StartWithHandler(handler func(*DelayingDelivererItem)) {
go func() {
for {
select {
case item := <-d.targetChannel:
handler(item)
case <-d.stopChannel:
return
}
}
}()
d.Start()
}

View File

@@ -0,0 +1,570 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"reflect"
"sync"
"time"
"github.com/pkg/errors"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/klog"
fedcommon "sigs.k8s.io/kubefed/pkg/apis/core/common"
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
"sigs.k8s.io/kubefed/pkg/client/generic"
)
const (
clusterSyncPeriod = 10 * time.Minute
userAgentName = "kubefed-controller"
)
// An object with an origin information.
type FederatedObject struct {
Object interface{}
ClusterName string
}
// FederatedReadOnlyStore is an overlay over multiple stores created in federated clusters.
type FederatedReadOnlyStore interface {
// Returns all items in the store.
List() ([]FederatedObject, error)
// Returns all items from a cluster.
ListFromCluster(clusterName string) ([]interface{}, error)
// GetKeyFor returns the key under which the item would be put in the store.
GetKeyFor(item interface{}) string
// GetByKey returns the item stored under the given key in the specified cluster (if exist).
GetByKey(clusterName string, key string) (interface{}, bool, error)
// Returns the items stored under the given key in all clusters.
GetFromAllClusters(key string) ([]FederatedObject, error)
// Checks whether stores for all clusters form the lists (and only these) are there and
// are synced. This is only a basic check whether the data inside of the store is usable.
// It is not a full synchronization/locking mechanism it only tries to ensure that out-of-sync
// issues occur less often. All users of the interface should assume
// that there may be significant delays in content updates of all kinds and write their
// code that it doesn't break if something is slightly out-of-sync.
ClustersSynced(clusters []*fedv1b1.KubeFedCluster) bool
}
// An interface to retrieve both KubeFedCluster resources and clients
// to access the clusters they represent.
type RegisteredClustersView interface {
// GetClientForCluster returns a client for the cluster, if present.
GetClientForCluster(clusterName string) (generic.Client, error)
// GetUnreadyClusters returns a list of all clusters that are not ready yet.
GetUnreadyClusters() ([]*fedv1b1.KubeFedCluster, error)
// GetReadyClusters returns all clusters for which the sub-informers are run.
GetReadyClusters() ([]*fedv1b1.KubeFedCluster, error)
// GetClusters returns a list of all clusters.
GetClusters() ([]*fedv1b1.KubeFedCluster, error)
// GetReadyCluster returns the cluster with the given name, if found.
GetReadyCluster(name string) (*fedv1b1.KubeFedCluster, bool, error)
// ClustersSynced returns true if the view is synced (for the first time).
ClustersSynced() bool
}
// FederatedInformer provides access to clusters registered with a
// KubeFed control plane and watches a given resource type in
// registered clusters.
//
// Whenever a new cluster is registered with KubeFed, an informer is
// created for it using TargetInformerFactory. Informers are stopped
// when a cluster is either put offline of deleted. It is assumed that
// some controller keeps an eye on the cluster list and thus the
// clusters in ETCD are up to date.
type FederatedInformer interface {
RegisteredClustersView
// Returns a store created over all stores from target informers.
GetTargetStore() FederatedReadOnlyStore
// Starts all the processes.
Start()
// Stops all the processes inside the informer.
Stop()
}
// A function that should be used to create an informer on the target object. Store should use
// cache.DeletionHandlingMetaNamespaceKeyFunc as a keying function.
type TargetInformerFactory func(*fedv1b1.KubeFedCluster, *restclient.Config) (cache.Store, cache.Controller, error)
// A structure with cluster lifecycle handler functions. Cluster is available (and ClusterAvailable is fired)
// when it is created in federated etcd and ready. Cluster becomes unavailable (and ClusterUnavailable is fired)
// when it is either deleted or becomes not ready. When cluster spec (IP)is modified both ClusterAvailable
// and ClusterUnavailable are fired.
type ClusterLifecycleHandlerFuncs struct {
// Fired when the cluster becomes available.
ClusterAvailable func(*fedv1b1.KubeFedCluster)
// Fired when the cluster becomes unavailable. The second arg contains data that was present
// in the cluster before deletion.
ClusterUnavailable func(*fedv1b1.KubeFedCluster, []interface{})
}
// Builds a FederatedInformer for the given configuration.
func NewFederatedInformer(
config *ControllerConfig,
client generic.Client,
apiResource *metav1.APIResource,
triggerFunc func(pkgruntime.Object),
clusterLifecycle *ClusterLifecycleHandlerFuncs) (FederatedInformer, error) {
targetInformerFactory := func(cluster *fedv1b1.KubeFedCluster, clusterConfig *restclient.Config) (cache.Store, cache.Controller, error) {
resourceClient, err := NewResourceClient(clusterConfig, apiResource)
if err != nil {
return nil, nil, err
}
targetNamespace := NamespaceForCluster(cluster.Name, config.TargetNamespace)
store, controller := NewManagedResourceInformer(resourceClient, targetNamespace, apiResource, triggerFunc)
return store, controller, nil
}
federatedInformer := &federatedInformerImpl{
targetInformerFactory: targetInformerFactory,
configFactory: func(cluster *fedv1b1.KubeFedCluster) (*restclient.Config, error) {
clusterConfig, err := BuildClusterConfig(cluster, client, config.KubeFedNamespace)
if err != nil {
return nil, err
}
if clusterConfig == nil {
return nil, errors.Errorf("Unable to load configuration for cluster %q", cluster.Name)
}
restclient.AddUserAgent(clusterConfig, userAgentName)
return clusterConfig, nil
},
targetInformers: make(map[string]informer),
fedNamespace: config.KubeFedNamespace,
clusterClients: make(map[string]generic.Client),
}
getClusterData := func(name string) []interface{} {
data, err := federatedInformer.GetTargetStore().ListFromCluster(name)
if err != nil {
klog.Errorf("Failed to list %s content: %v", name, err)
return make([]interface{}, 0)
}
return data
}
var err error
federatedInformer.clusterInformer.store, federatedInformer.clusterInformer.controller, err = NewGenericInformerWithEventHandler(
config.KubeConfig,
config.KubeFedNamespace,
&fedv1b1.KubeFedCluster{},
clusterSyncPeriod,
&cache.ResourceEventHandlerFuncs{
DeleteFunc: func(old interface{}) {
oldCluster, ok := old.(*fedv1b1.KubeFedCluster)
if ok {
var data []interface{}
if clusterLifecycle.ClusterUnavailable != nil {
data = getClusterData(oldCluster.Name)
}
federatedInformer.deleteCluster(oldCluster)
if clusterLifecycle.ClusterUnavailable != nil {
clusterLifecycle.ClusterUnavailable(oldCluster, data)
}
}
},
AddFunc: func(cur interface{}) {
curCluster, ok := cur.(*fedv1b1.KubeFedCluster)
if !ok {
klog.Errorf("Cluster %v/%v not added; incorrect type", curCluster.Namespace, curCluster.Name)
} else if IsClusterReady(&curCluster.Status) {
federatedInformer.addCluster(curCluster)
klog.Infof("Cluster %v/%v is ready", curCluster.Namespace, curCluster.Name)
if clusterLifecycle.ClusterAvailable != nil {
clusterLifecycle.ClusterAvailable(curCluster)
}
} else {
klog.Infof("Cluster %v/%v not added; it is not ready.", curCluster.Namespace, curCluster.Name)
}
},
UpdateFunc: func(old, cur interface{}) {
oldCluster, ok := old.(*fedv1b1.KubeFedCluster)
if !ok {
klog.Errorf("Internal error: Cluster %v not updated. Old cluster not of correct type.", old)
return
}
curCluster, ok := cur.(*fedv1b1.KubeFedCluster)
if !ok {
klog.Errorf("Internal error: Cluster %v not updated. New cluster not of correct type.", cur)
return
}
if IsClusterReady(&oldCluster.Status) != IsClusterReady(&curCluster.Status) || !reflect.DeepEqual(oldCluster.Spec, curCluster.Spec) || !reflect.DeepEqual(oldCluster.ObjectMeta.Labels, curCluster.ObjectMeta.Labels) || !reflect.DeepEqual(oldCluster.ObjectMeta.Annotations, curCluster.ObjectMeta.Annotations) {
var data []interface{}
if clusterLifecycle.ClusterUnavailable != nil {
data = getClusterData(oldCluster.Name)
}
federatedInformer.deleteCluster(oldCluster)
if clusterLifecycle.ClusterUnavailable != nil {
clusterLifecycle.ClusterUnavailable(oldCluster, data)
}
if IsClusterReady(&curCluster.Status) {
federatedInformer.addCluster(curCluster)
if clusterLifecycle.ClusterAvailable != nil {
clusterLifecycle.ClusterAvailable(curCluster)
}
}
} else {
klog.V(7).Infof("Cluster %v not updated to %v as ready status and specs are identical", oldCluster, curCluster)
}
},
},
)
return federatedInformer, err
}
func IsClusterReady(clusterStatus *fedv1b1.KubeFedClusterStatus) bool {
for _, condition := range clusterStatus.Conditions {
if condition.Type == fedcommon.ClusterReady {
if condition.Status == apiv1.ConditionTrue {
return true
}
}
}
return false
}
type informer struct {
controller cache.Controller
store cache.Store
stopChan chan struct{}
}
type federatedInformerImpl struct {
sync.Mutex
// Informer on federated clusters.
clusterInformer informer
// Target informers factory
targetInformerFactory TargetInformerFactory
// Structures returned by targetInformerFactory
targetInformers map[string]informer
// Retrieves configuration to access a cluster.
configFactory func(*fedv1b1.KubeFedCluster) (*restclient.Config, error)
// Caches cluster clients (reduces client discovery and secret retrieval)
clusterClients map[string]generic.Client
// Namespace from which to source KubeFedCluster resources
fedNamespace string
}
// *federatedInformerImpl implements FederatedInformer interface.
var _ FederatedInformer = &federatedInformerImpl{}
type federatedStoreImpl struct {
federatedInformer *federatedInformerImpl
}
func (f *federatedInformerImpl) Stop() {
klog.V(4).Infof("Stopping federated informer.")
f.Lock()
defer f.Unlock()
klog.V(4).Infof("... Closing cluster informer channel.")
close(f.clusterInformer.stopChan)
for key, informer := range f.targetInformers {
klog.V(4).Infof("... Closing informer channel for %q.", key)
close(informer.stopChan)
// Remove each informer after it has been stopped to prevent
// subsequent cluster deletion from attempting to double close
// an informer's stop channel.
delete(f.targetInformers, key)
}
}
func (f *federatedInformerImpl) Start() {
f.Lock()
defer f.Unlock()
f.clusterInformer.stopChan = make(chan struct{})
go f.clusterInformer.controller.Run(f.clusterInformer.stopChan)
}
// GetClientForCluster returns a client for the cluster, if present.
func (f *federatedInformerImpl) GetClientForCluster(clusterName string) (generic.Client, error) {
f.Lock()
defer f.Unlock()
// return cached client if one exists (to prevent frequent secret retrieval and rest discovery)
if client, ok := f.clusterClients[clusterName]; ok {
return client, nil
}
config, err := f.getConfigForClusterUnlocked(clusterName)
if err != nil {
return nil, errors.Wrap(err, "Client creation failed")
}
client, err := generic.New(config)
if err != nil {
return client, err
}
f.clusterClients[clusterName] = client
return client, nil
}
func (f *federatedInformerImpl) getConfigForClusterUnlocked(clusterName string) (*restclient.Config, error) {
// No locking needed. Will happen in f.GetCluster.
klog.V(4).Infof("Getting config for cluster %q", clusterName)
if cluster, found, err := f.getReadyClusterUnlocked(clusterName); found && err == nil {
return f.configFactory(cluster)
} else {
if err != nil {
return nil, err
}
}
return nil, errors.Errorf("cluster %q not found", clusterName)
}
func (f *federatedInformerImpl) GetUnreadyClusters() ([]*fedv1b1.KubeFedCluster, error) {
f.Lock()
defer f.Unlock()
items := f.clusterInformer.store.List()
result := make([]*fedv1b1.KubeFedCluster, 0, len(items))
for _, item := range items {
if cluster, ok := item.(*fedv1b1.KubeFedCluster); ok {
if !IsClusterReady(&cluster.Status) {
result = append(result, cluster)
}
} else {
return nil, errors.Errorf("wrong data in FederatedInformerImpl cluster store: %v", item)
}
}
return result, nil
}
// GetReadyClusters returns all clusters for which the sub-informers are run.
func (f *federatedInformerImpl) GetReadyClusters() ([]*fedv1b1.KubeFedCluster, error) {
return f.getClusters(true)
}
// GetClusters returns all clusters regardless of ready state.
func (f *federatedInformerImpl) GetClusters() ([]*fedv1b1.KubeFedCluster, error) {
return f.getClusters(false)
}
// GetReadyClusters returns only ready clusters if onlyReady is true and all clusters otherwise.
func (f *federatedInformerImpl) getClusters(onlyReady bool) ([]*fedv1b1.KubeFedCluster, error) {
f.Lock()
defer f.Unlock()
items := f.clusterInformer.store.List()
result := make([]*fedv1b1.KubeFedCluster, 0, len(items))
for _, item := range items {
if cluster, ok := item.(*fedv1b1.KubeFedCluster); ok {
if !onlyReady || IsClusterReady(&cluster.Status) {
result = append(result, cluster)
}
} else {
return nil, errors.Errorf("wrong data in FederatedInformerImpl cluster store: %v", item)
}
}
return result, nil
}
// GetCluster returns the cluster with the given name, if found.
func (f *federatedInformerImpl) GetReadyCluster(name string) (*fedv1b1.KubeFedCluster, bool, error) {
f.Lock()
defer f.Unlock()
return f.getReadyClusterUnlocked(name)
}
func (f *federatedInformerImpl) getReadyClusterUnlocked(name string) (*fedv1b1.KubeFedCluster, bool, error) {
key := fmt.Sprintf("%s/%s", f.fedNamespace, name)
if obj, exist, err := f.clusterInformer.store.GetByKey(key); exist && err == nil {
if cluster, ok := obj.(*fedv1b1.KubeFedCluster); ok {
if IsClusterReady(&cluster.Status) {
return cluster, true, nil
}
return nil, false, nil
}
return nil, false, errors.Errorf("wrong data in FederatedInformerImpl cluster store: %v", obj)
} else {
return nil, false, err
}
}
// Synced returns true if the view is synced (for the first time)
func (f *federatedInformerImpl) ClustersSynced() bool {
return f.clusterInformer.controller.HasSynced()
}
// Adds the given cluster to federated informer.
func (f *federatedInformerImpl) addCluster(cluster *fedv1b1.KubeFedCluster) {
f.Lock()
defer f.Unlock()
name := cluster.Name
if config, err := f.getConfigForClusterUnlocked(name); err == nil {
store, controller, err := f.targetInformerFactory(cluster, config)
if err != nil {
// TODO: create also an event for cluster.
klog.Errorf("Failed to create an informer for cluster %q: %v", cluster.Name, err)
return
}
targetInformer := informer{
controller: controller,
store: store,
stopChan: make(chan struct{}),
}
f.targetInformers[name] = targetInformer
go targetInformer.controller.Run(targetInformer.stopChan)
} else {
// TODO: create also an event for cluster.
klog.Errorf("Failed to create a client for cluster: %v", err)
}
}
// Removes the cluster from federated informer.
func (f *federatedInformerImpl) deleteCluster(cluster *fedv1b1.KubeFedCluster) {
f.Lock()
defer f.Unlock()
name := cluster.Name
if targetInformer, found := f.targetInformers[name]; found {
close(targetInformer.stopChan)
}
delete(f.targetInformers, name)
delete(f.clusterClients, name)
}
// Returns a store created over all stores from target informers.
func (f *federatedInformerImpl) GetTargetStore() FederatedReadOnlyStore {
return &federatedStoreImpl{
federatedInformer: f,
}
}
// Returns all items in the store.
func (fs *federatedStoreImpl) List() ([]FederatedObject, error) {
fs.federatedInformer.Lock()
defer fs.federatedInformer.Unlock()
result := make([]FederatedObject, 0)
for clusterName, targetInformer := range fs.federatedInformer.targetInformers {
for _, value := range targetInformer.store.List() {
result = append(result, FederatedObject{ClusterName: clusterName, Object: value})
}
}
return result, nil
}
// Returns all items in the given cluster.
func (fs *federatedStoreImpl) ListFromCluster(clusterName string) ([]interface{}, error) {
fs.federatedInformer.Lock()
defer fs.federatedInformer.Unlock()
result := make([]interface{}, 0)
if targetInformer, found := fs.federatedInformer.targetInformers[clusterName]; found {
values := targetInformer.store.List()
result = append(result, values...)
}
return result, nil
}
// GetByKey returns the item stored under the given key in the specified cluster (if exist).
func (fs *federatedStoreImpl) GetByKey(clusterName string, key string) (interface{}, bool, error) {
fs.federatedInformer.Lock()
defer fs.federatedInformer.Unlock()
if targetInformer, found := fs.federatedInformer.targetInformers[clusterName]; found {
return targetInformer.store.GetByKey(key)
}
return nil, false, nil
}
// Returns the items stored under the given key in all clusters.
func (fs *federatedStoreImpl) GetFromAllClusters(key string) ([]FederatedObject, error) {
fs.federatedInformer.Lock()
defer fs.federatedInformer.Unlock()
result := make([]FederatedObject, 0)
for clusterName, targetInformer := range fs.federatedInformer.targetInformers {
value, exist, err := targetInformer.store.GetByKey(key)
if err != nil {
return nil, err
}
if exist {
result = append(result, FederatedObject{ClusterName: clusterName, Object: value})
}
}
return result, nil
}
// GetKeyFor returns the key under which the item would be put in the store.
func (fs *federatedStoreImpl) GetKeyFor(item interface{}) string {
// TODO: support other keying functions.
key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(item)
return key
}
// Checks whether stores for all clusters form the lists (and only these) are there and
// are synced.
func (fs *federatedStoreImpl) ClustersSynced(clusters []*fedv1b1.KubeFedCluster) bool {
// Get the list of informers to check under a lock and check it outside.
okSoFar, informersToCheck := func() (bool, []informer) {
fs.federatedInformer.Lock()
defer fs.federatedInformer.Unlock()
if len(fs.federatedInformer.targetInformers) != len(clusters) {
return false, []informer{}
}
informersToCheck := make([]informer, 0, len(clusters))
for _, cluster := range clusters {
if targetInformer, found := fs.federatedInformer.targetInformers[cluster.Name]; found {
informersToCheck = append(informersToCheck, targetInformer)
} else {
return false, []informer{}
}
}
return true, informersToCheck
}()
if !okSoFar {
return false
}
for _, informerToCheck := range informersToCheck {
if !informerToCheck.controller.HasSynced() {
return false
}
}
return true
}

View File

@@ -0,0 +1,35 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// FederatedResource is a generic representation of a federated type
type FederatedResource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
ClusterStatus []ResourceClusterStatus `json:"clusterStatus,omitempty"`
}
// ResourceClusterStatus defines the status of federated resource within a cluster
type ResourceClusterStatus struct {
ClusterName string `json:"clusterName,omitempty"`
Status map[string]interface{} `json:"status,omitempty"`
}

View File

@@ -0,0 +1,86 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"time"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/kubefed/pkg/client/generic/scheme"
)
func NewGenericInformer(config *rest.Config, namespace string, obj pkgruntime.Object, resyncPeriod time.Duration, triggerFunc func(pkgruntime.Object)) (cache.Store, cache.Controller, error) {
return NewGenericInformerWithEventHandler(config, namespace, obj, resyncPeriod, NewTriggerOnAllChanges(triggerFunc))
}
func NewGenericInformerWithEventHandler(config *rest.Config, namespace string, obj pkgruntime.Object, resyncPeriod time.Duration, resourceEventHandlerFuncs *cache.ResourceEventHandlerFuncs) (cache.Store, cache.Controller, error) {
gvk, err := apiutil.GVKForObject(obj, scheme.Scheme)
if err != nil {
return nil, nil, err
}
mapper, err := apiutil.NewDiscoveryRESTMapper(config)
if err != nil {
return nil, nil, errors.Wrap(err, "Could not create RESTMapper from config")
}
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, nil, err
}
client, err := apiutil.RESTClientForGVK(gvk, config, scheme.Codecs)
if err != nil {
return nil, nil, err
}
listGVK := gvk.GroupVersion().WithKind(gvk.Kind + "List")
listObj, err := scheme.Scheme.New(listGVK)
if err != nil {
return nil, nil, err
}
store, controller := cache.NewInformer(
&cache.ListWatch{
ListFunc: func(opts metav1.ListOptions) (pkgruntime.Object, error) {
res := listObj.DeepCopyObject()
isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
err := client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, scheme.ParameterCodec).Do().Into(res)
return res, err
},
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
// Watch needs to be set to true separately
opts.Watch = true
isNamespaceScoped := namespace != "" && mapping.Scope.Name() != meta.RESTScopeNameRoot
return client.Get().NamespaceIfScoped(namespace, isNamespaceScoped).Resource(mapping.Resource.Resource).VersionedParams(&opts, scheme.ParameterCodec).Watch()
},
},
obj,
resyncPeriod,
resourceEventHandlerFuncs,
)
return store, controller, nil
}

View File

@@ -0,0 +1,52 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"reflect"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
)
// Returns cache.ResourceEventHandlerFuncs that trigger the given function
// on all object changes.
func NewTriggerOnAllChanges(triggerFunc func(pkgruntime.Object)) *cache.ResourceEventHandlerFuncs {
return &cache.ResourceEventHandlerFuncs{
DeleteFunc: func(old interface{}) {
if deleted, ok := old.(cache.DeletedFinalStateUnknown); ok {
// This object might be stale but ok for our current usage.
old = deleted.Obj
if old == nil {
return
}
}
oldObj := old.(pkgruntime.Object)
triggerFunc(oldObj)
},
AddFunc: func(cur interface{}) {
curObj := cur.(pkgruntime.Object)
triggerFunc(curObj)
},
UpdateFunc: func(old, cur interface{}) {
curObj := cur.(pkgruntime.Object)
if !reflect.DeepEqual(old, cur) {
triggerFunc(curObj)
}
},
}
}

View File

@@ -0,0 +1,69 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
const (
ManagedByKubeFedLabelKey = "kubefed.io/managed"
ManagedByKubeFedLabelValue = "true"
UnmanagedByKubeFedLabelValue = "false"
)
// HasManagedLabel indicates whether the given object has the managed
// label.
func HasManagedLabel(obj *unstructured.Unstructured) bool {
labels := obj.GetLabels()
if labels == nil {
return false
}
return labels[ManagedByKubeFedLabelKey] == ManagedByKubeFedLabelValue
}
// IsExplicitlyUnmanaged indicates whether the given object has the managed
// label with value false.
func IsExplicitlyUnmanaged(obj *unstructured.Unstructured) bool {
labels := obj.GetLabels()
if labels == nil {
return false
}
return labels[ManagedByKubeFedLabelKey] == UnmanagedByKubeFedLabelValue
}
// AddManagedLabel ensures that the given object has the managed
// label.
func AddManagedLabel(obj *unstructured.Unstructured) {
labels := obj.GetLabels()
if labels == nil {
labels = make(map[string]string)
}
labels[ManagedByKubeFedLabelKey] = ManagedByKubeFedLabelValue
obj.SetLabels(labels)
}
// RemoveManagedLabel ensures that the given object does not have the
// managed label.
func RemoveManagedLabel(obj *unstructured.Unstructured) {
labels := obj.GetLabels()
if labels == nil || labels[ManagedByKubeFedLabelKey] != ManagedByKubeFedLabelValue {
return
}
delete(labels, ManagedByKubeFedLabelKey)
obj.SetLabels(labels)
}

139
vendor/sigs.k8s.io/kubefed/pkg/controller/util/meta.go generated vendored Normal file
View File

@@ -0,0 +1,139 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"encoding/json"
"reflect"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
)
// Copies cluster-independent, user provided data from the given ObjectMeta struct. If in
// the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
func copyObjectMeta(obj metav1.ObjectMeta) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: obj.Name,
Namespace: obj.Namespace,
Labels: obj.Labels,
Annotations: obj.Annotations,
ResourceVersion: obj.ResourceVersion,
}
}
// Deep copies cluster-independent, user provided data from the given ObjectMeta struct. If in
// the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
func DeepCopyRelevantObjectMeta(obj metav1.ObjectMeta) metav1.ObjectMeta {
copyMeta := copyObjectMeta(obj)
if obj.Labels != nil {
copyMeta.Labels = make(map[string]string)
for key, val := range obj.Labels {
copyMeta.Labels[key] = val
}
}
if obj.Annotations != nil {
copyMeta.Annotations = make(map[string]string)
for key, val := range obj.Annotations {
copyMeta.Annotations[key] = val
}
}
return copyMeta
}
// Checks if cluster-independent, user provided data in two given ObjectMeta are equal. If in
// the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
func ObjectMetaEquivalent(a, b metav1.ObjectMeta) bool {
if a.Name != b.Name {
return false
}
if a.Namespace != b.Namespace {
return false
}
if !reflect.DeepEqual(a.Labels, b.Labels) && (len(a.Labels) != 0 || len(b.Labels) != 0) {
return false
}
if !reflect.DeepEqual(a.Annotations, b.Annotations) && (len(a.Annotations) != 0 || len(b.Annotations) != 0) {
return false
}
return true
}
// Checks if cluster-independent, user provided data in two given ObjectMeta are equal. If in
// the future the ObjectMeta structure is expanded then any field that is not populated
// by the api server should be included here.
func ObjectMetaObjEquivalent(a, b metav1.Object) bool {
if a.GetName() != b.GetName() {
return false
}
if a.GetNamespace() != b.GetNamespace() {
return false
}
aLabels := a.GetLabels()
bLabels := b.GetLabels()
if !reflect.DeepEqual(aLabels, bLabels) && (len(aLabels) != 0 || len(bLabels) != 0) {
return false
}
aAnnotations := a.GetAnnotations()
bAnnotations := b.GetAnnotations()
if !reflect.DeepEqual(aAnnotations, bAnnotations) && (len(aAnnotations) != 0 || len(bAnnotations) != 0) {
return false
}
return true
}
// Checks if cluster-independent, user provided data in ObjectMeta and Spec in two given top
// level api objects are equivalent.
func ObjectMetaAndSpecEquivalent(a, b runtime.Object) bool {
objectMetaA := reflect.ValueOf(a).Elem().FieldByName("ObjectMeta").Interface().(metav1.ObjectMeta)
objectMetaB := reflect.ValueOf(b).Elem().FieldByName("ObjectMeta").Interface().(metav1.ObjectMeta)
specA := reflect.ValueOf(a).Elem().FieldByName("Spec").Interface()
specB := reflect.ValueOf(b).Elem().FieldByName("Spec").Interface()
return ObjectMetaEquivalent(objectMetaA, objectMetaB) && reflect.DeepEqual(specA, specB)
}
func MetaAccessor(obj pkgruntime.Object) metav1.Object {
accessor, err := meta.Accessor(obj)
if err != nil {
// This should always succeed if obj is not nil. Also,
// adapters are slated for replacement by unstructured.
return nil
}
return accessor
}
// GetUnstructured return Unstructured for any given kubernetes type
func GetUnstructured(resource interface{}) (*unstructured.Unstructured, error) {
content, err := json.Marshal(resource)
if err != nil {
return nil, errors.Wrap(err, "Failed to JSON Marshal")
}
unstructuredResource := &unstructured.Unstructured{}
err = unstructuredResource.UnmarshalJSON(content)
if err != nil {
return nil, errors.Wrap(err, "Failed to UnmarshalJSON into unstructured content")
}
return unstructuredResource, nil
}

View File

@@ -0,0 +1,47 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
// The functions in this file are exposed as variables to allow them
// to be overridden for testing purposes. Simulated scale testing
// requires being able to change the namespace of target resources
// (NamespaceForCluster and QualifiedNameForCluster) and ensure that
// the namespace of a federated resource will always be the kubefed
// system namespace (NamespaceForResource).
func namespaceForCluster(clusterName, namespace string) string {
return namespace
}
// NamespaceForCluster returns the namespace to use for the given cluster.
var NamespaceForCluster = namespaceForCluster
func namespaceForResource(resourceNamespace, fedNamespace string) string {
return resourceNamespace
}
// NamespaceForResource returns either the kubefed namespace or
// resource namespace.
var NamespaceForResource = namespaceForResource
func qualifiedNameForCluster(clusterName string, qualifiedName QualifiedName) QualifiedName {
return qualifiedName
}
// QualifiedNameForCluster returns the qualified name to use for the
// given cluster.
var QualifiedNameForCluster = qualifiedNameForCluster

View File

@@ -0,0 +1,58 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
const (
// If this annotation is present on a federated resource, resources in the
// member clusters managed by the federated resource should be orphaned.
// If the annotation is not present (the default), resources in member
// clusters will be deleted before the federated resource is deleted.
OrphanManagedResourcesAnnotation = "kubefed.io/orphan"
OrphanedManagedResourcesValue = "true"
)
// IsOrphaningEnabled checks status of "orphaning enable" (OrphanManagedResources: OrphanedManagedResourceslValue')
// annotation on a resource.
func IsOrphaningEnabled(obj *unstructured.Unstructured) bool {
annotations := obj.GetAnnotations()
if annotations == nil {
return false
}
return annotations[OrphanManagedResourcesAnnotation] == OrphanedManagedResourcesValue
}
// Enables the orphaning mode
func EnableOrphaning(obj *unstructured.Unstructured) {
annotations := obj.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations[OrphanManagedResourcesAnnotation] = OrphanedManagedResourcesValue
obj.SetAnnotations(annotations)
}
// Disables the orphaning mode
func DisableOrphaning(obj *unstructured.Unstructured) {
annotations := obj.GetAnnotations()
if annotations == nil {
return
}
delete(annotations, OrphanManagedResourcesAnnotation)
obj.SetAnnotations(annotations)
}

View File

@@ -0,0 +1,192 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"encoding/json"
"github.com/evanphx/json-patch"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/sets"
)
type ClusterOverride struct {
Op string `json:"op,omitempty"`
Path string `json:"path"`
Value interface{} `json:"value,omitempty"`
}
type GenericOverrideItem struct {
ClusterName string `json:"clusterName"`
ClusterOverrides []ClusterOverride `json:"clusterOverrides,omitempty"`
}
type GenericOverrideSpec struct {
Overrides []GenericOverrideItem `json:"overrides,omitempty"`
}
type GenericOverride struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec *GenericOverrideSpec `json:"spec,omitempty"`
}
// Namespace and name may not be overridden since these fields are the
// primary mechanism of association between a federated resource in
// the host cluster and the target resources in the member clusters.
//
// Kind should always be sourced from the FTC and not vary across
// member clusters.
//
// apiVersion can be overridden to support managing resources like
// Ingress which can exist in different groups at different
// versions. Users will need to take care not to abuse this
// capability.
var invalidPaths = sets.NewString(
"/metadata/namespace",
"/metadata/name",
"/metadata/generateName",
"/kind",
)
// Slice of ClusterOverride
type ClusterOverrides []ClusterOverride
// Mapping of clusterName to overrides for the cluster
type OverridesMap map[string]ClusterOverrides
// ToUnstructuredSlice converts the map of overrides to a slice of
// interfaces that can be set in an unstructured object.
func (m OverridesMap) ToUnstructuredSlice() []interface{} {
overrides := []interface{}{}
for clusterName, clusterOverrides := range m {
overridesItem := map[string]interface{}{
ClusterNameField: clusterName,
ClusterOverridesField: clusterOverrides,
}
overrides = append(overrides, overridesItem)
}
return overrides
}
// GetOverrides returns a map of overrides populated from the given
// unstructured object.
func GetOverrides(rawObj *unstructured.Unstructured) (OverridesMap, error) {
overridesMap := make(OverridesMap)
if rawObj == nil {
return overridesMap, nil
}
genericFedObject := GenericOverride{}
err := UnstructuredToInterface(rawObj, &genericFedObject)
if err != nil {
return nil, err
}
if genericFedObject.Spec == nil || genericFedObject.Spec.Overrides == nil {
// No overrides defined for the federated type
return overridesMap, nil
}
for _, overrideItem := range genericFedObject.Spec.Overrides {
clusterName := overrideItem.ClusterName
if _, ok := overridesMap[clusterName]; ok {
return nil, errors.Errorf("cluster %q appears more than once", clusterName)
}
clusterOverrides := overrideItem.ClusterOverrides
paths := sets.NewString()
for i, clusterOverride := range clusterOverrides {
path := clusterOverride.Path
if invalidPaths.Has(path) {
return nil, errors.Errorf("override[%d] for cluster %q has an invalid path: %s", i, clusterName, path)
}
if paths.Has(path) {
return nil, errors.Errorf("path %q appears more than once for cluster %q", path, clusterName)
}
paths.Insert(path)
}
overridesMap[clusterName] = clusterOverrides
}
return overridesMap, nil
}
// SetOverrides sets the spec.overrides field of the unstructured
// object from the provided overrides map.
func SetOverrides(fedObject *unstructured.Unstructured, overridesMap OverridesMap) error {
rawSpec := fedObject.Object[SpecField]
if rawSpec == nil {
rawSpec = map[string]interface{}{}
fedObject.Object[SpecField] = rawSpec
}
spec, ok := rawSpec.(map[string]interface{})
if !ok {
return errors.Errorf("Unable to set overrides since %q is not an object: %T", SpecField, rawSpec)
}
spec[OverridesField] = overridesMap.ToUnstructuredSlice()
return nil
}
// UnstructuredToInterface converts an unstructured object to the
// provided interface by json marshalling/unmarshalling.
func UnstructuredToInterface(rawObj *unstructured.Unstructured, obj interface{}) error {
content, err := rawObj.MarshalJSON()
if err != nil {
return err
}
return json.Unmarshal(content, obj)
}
// ApplyJsonPatch applies the override on to the given unstructured object.
func ApplyJsonPatch(obj *unstructured.Unstructured, overrides ClusterOverrides) error {
// TODO: Do the defaulting of "op" field to "replace" in API defaulting
for i, overrideItem := range overrides {
if overrideItem.Op == "" {
overrides[i].Op = "replace"
}
}
jsonPatchBytes, err := json.Marshal(overrides)
if err != nil {
return err
}
patch, err := jsonpatch.DecodePatch(jsonPatchBytes)
if err != nil {
return err
}
ObjectJSONBytes, err := obj.MarshalJSON()
if err != nil {
return err
}
patchedObjectJSONBytes, err := patch.Apply(ObjectJSONBytes)
if err != nil {
return err
}
err = obj.UnmarshalJSON(patchedObjectJSONBytes)
return err
}

View File

@@ -0,0 +1,88 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
)
type GenericClusterReference struct {
Name string `json:"name"`
}
type GenericPlacementFields struct {
Clusters []GenericClusterReference `json:"clusters,omitempty"`
ClusterSelector *metav1.LabelSelector `json:"clusterSelector,omitempty"`
}
type GenericPlacementSpec struct {
Placement GenericPlacementFields `json:"placement,omitempty"`
}
type GenericPlacement struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec GenericPlacementSpec `json:"spec,omitempty"`
}
func UnmarshalGenericPlacement(obj *unstructured.Unstructured) (*GenericPlacement, error) {
placement := &GenericPlacement{}
err := UnstructuredToInterface(obj, placement)
if err != nil {
return nil, err
}
return placement, nil
}
func (p *GenericPlacement) ClusterNames() []string {
if p.Spec.Placement.Clusters == nil {
return nil
}
clusterNames := []string{}
for _, cluster := range p.Spec.Placement.Clusters {
clusterNames = append(clusterNames, cluster.Name)
}
return clusterNames
}
func (p *GenericPlacement) ClusterSelector() (labels.Selector, error) {
return metav1.LabelSelectorAsSelector(p.Spec.Placement.ClusterSelector)
}
func GetClusterNames(obj *unstructured.Unstructured) ([]string, error) {
placement, err := UnmarshalGenericPlacement(obj)
if err != nil {
return nil, err
}
return placement.ClusterNames(), nil
}
func SetClusterNames(obj *unstructured.Unstructured, clusterNames []string) error {
var clusters []interface{}
if clusterNames != nil {
clusters = []interface{}{}
for _, clusterName := range clusterNames {
clusters = append(clusters, map[string]interface{}{
NameField: clusterName,
})
}
}
return unstructured.SetNestedSlice(obj.Object, clusters, SpecField, PlacementField, ClustersField)
}

View File

@@ -0,0 +1,76 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"reflect"
"sort"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
fedv1a1 "sigs.k8s.io/kubefed/pkg/apis/core/v1alpha1"
)
const (
generationPrefix = "gen:"
resourceVersionPrefix = "rv:"
)
// ObjectVersion retrieves the field type-prefixed value used for
// determining currency of the given cluster object.
func ObjectVersion(clusterObj *unstructured.Unstructured) string {
generation := clusterObj.GetGeneration()
if generation != 0 {
return fmt.Sprintf("%s%d", generationPrefix, generation)
}
return fmt.Sprintf("%s%s", resourceVersionPrefix, clusterObj.GetResourceVersion())
}
// ObjectNeedsUpdate determines whether the 2 objects provided cluster
// object needs to be updated according to the desired object and the
// recorded version.
func ObjectNeedsUpdate(desiredObj, clusterObj *unstructured.Unstructured, recordedVersion string) bool {
targetVersion := ObjectVersion(clusterObj)
if recordedVersion != targetVersion {
return true
}
// If versions match and the version is sourced from the
// generation field, a further check of metadata equivalency is
// required.
return strings.HasPrefix(targetVersion, generationPrefix) && !ObjectMetaObjEquivalent(desiredObj, clusterObj)
}
// SortClusterVersions ASCII sorts the given cluster versions slice
// based on cluster name.
func SortClusterVersions(versions []fedv1a1.ClusterObjectVersion) {
sort.Slice(versions, func(i, j int) bool {
return versions[i].ClusterName < versions[j].ClusterName
})
}
// PropagatedVersionStatusEquivalent returns true if both statuses are equal by
// comparing Template and Override version, and their ClusterVersion slices;
// false otherwise.
func PropagatedVersionStatusEquivalent(pvs1, pvs2 *fedv1a1.PropagatedVersionStatus) bool {
return pvs1.TemplateVersion == pvs2.TemplateVersion &&
pvs1.OverrideVersion == pvs2.OverrideVersion &&
reflect.DeepEqual(pvs1.ClusterVersions, pvs2.ClusterVersions)
}

View File

@@ -0,0 +1,57 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
meta "k8s.io/apimachinery/pkg/api/meta"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
)
// QualifiedName comprises a resource name with an optional namespace.
// If namespace is provided, a QualifiedName will be rendered as
// "<namespace>/<name>". If not, it will be rendered as "name". This
// is intended to allow the FederatedTypeAdapter interface and its
// consumers to operate on both namespaces and namespace-qualified
// resources.
type QualifiedName struct {
Namespace string
Name string
}
func NewQualifiedName(obj pkgruntime.Object) QualifiedName {
accessor, err := meta.Accessor(obj)
if err != nil {
// TODO(marun) This should never happen, but if it does, the
// resulting empty name.
return QualifiedName{}
}
return QualifiedName{
Namespace: accessor.GetNamespace(),
Name: accessor.GetName(),
}
}
// String returns the general purpose string representation
func (n QualifiedName) String() string {
if len(n.Namespace) == 0 {
return n.Name
}
return fmt.Sprintf("%s/%s", n.Namespace, n.Name)
}

View File

@@ -0,0 +1,70 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
type ResourceClient interface {
Resources(namespace string) dynamic.ResourceInterface
Kind() string
}
type resourceClient struct {
client dynamic.Interface
apiResource schema.GroupVersionResource
namespaced bool
kind string
}
func NewResourceClient(config *rest.Config, apiResource *metav1.APIResource) (ResourceClient, error) {
resource := schema.GroupVersionResource{
Group: apiResource.Group,
Version: apiResource.Version,
Resource: apiResource.Name,
}
client, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}
return &resourceClient{
client: client,
apiResource: resource,
namespaced: apiResource.Namespaced,
kind: apiResource.Kind,
}, nil
}
func (c *resourceClient) Resources(namespace string) dynamic.ResourceInterface {
// TODO(marun) Consider returning Interface instead of
// ResourceInterface to allow callers to decide if they want to
// invoke Namespace(). Either that, or replace the use of
// ResourceClient with the controller-runtime generic client.
if c.namespaced {
return c.client.Resource(c.apiResource).Namespace(namespace)
}
return c.client.Resource(c.apiResource)
}
func (c *resourceClient) Kind() string {
return c.kind
}

View File

@@ -0,0 +1,90 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
)
// NewResourceInformer returns an unfiltered informer.
func NewResourceInformer(client ResourceClient, namespace string, apiResource *metav1.APIResource, triggerFunc func(pkgruntime.Object)) (cache.Store, cache.Controller) {
return newResourceInformer(client, namespace, apiResource, triggerFunc, "")
}
// NewManagedResourceInformer returns an informer limited to resources
// managed by KubeFed as indicated by labeling.
func NewManagedResourceInformer(client ResourceClient, namespace string, apiResource *metav1.APIResource, triggerFunc func(pkgruntime.Object)) (cache.Store, cache.Controller) {
labelSelector := labels.Set(map[string]string{ManagedByKubeFedLabelKey: ManagedByKubeFedLabelValue}).AsSelector().String()
return newResourceInformer(client, namespace, apiResource, triggerFunc, labelSelector)
}
func newResourceInformer(client ResourceClient, namespace string, apiResource *metav1.APIResource, triggerFunc func(pkgruntime.Object), labelSelector string) (cache.Store, cache.Controller) {
obj := &unstructured.Unstructured{}
if apiResource != nil {
gvk := schema.GroupVersionKind{Group: apiResource.Group, Version: apiResource.Version, Kind: apiResource.Kind}
obj.SetGroupVersionKind(gvk)
}
return cache.NewInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (pkgruntime.Object, error) {
options.LabelSelector = labelSelector
return client.Resources(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
options.LabelSelector = labelSelector
return client.Resources(namespace).Watch(options)
},
},
obj, // use an unstructured type with apiVersion / kind populated for informer logging purposes
NoResyncPeriod,
NewTriggerOnAllChanges(triggerFunc),
)
}
func ObjFromCache(store cache.Store, kind, key string) (*unstructured.Unstructured, error) {
obj, err := rawObjFromCache(store, kind, key)
if err != nil {
return nil, err
}
if obj == nil {
return nil, nil
}
return obj.(*unstructured.Unstructured), nil
}
func rawObjFromCache(store cache.Store, kind, key string) (pkgruntime.Object, error) {
cachedObj, exist, err := store.GetByKey(key)
if err != nil {
wrappedErr := errors.Wrapf(err, "Failed to query %s store for %q", kind, key)
runtime.HandleError(wrappedErr)
return nil, err
}
if !exist {
return nil, nil
}
return cachedObj.(pkgruntime.Object).DeepCopyObject(), nil
}

View File

@@ -0,0 +1,75 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"sync"
)
type SafeMap struct {
sync.RWMutex
m map[string]interface{}
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]interface{}),
}
}
func (s *SafeMap) Store(key string, value interface{}) {
s.Lock()
defer s.Unlock()
s.m[key] = value
}
func (s *SafeMap) Get(key string) (interface{}, bool) {
s.RLock()
defer s.RUnlock()
value, ok := s.m[key]
return value, ok
}
func (s *SafeMap) GetAll() []interface{} {
s.RLock()
defer s.RUnlock()
vals := []interface{}{}
for _, val := range s.m {
vals = append(vals, val)
}
return vals
}
func (s *SafeMap) Delete(key string) {
s.Lock()
defer s.Unlock()
delete(s.m, key)
}
func (s *SafeMap) DeleteAll() {
s.Lock()
defer s.Unlock()
for key := range s.m {
delete(s.m, key)
}
}
func (s *SafeMap) Size() int {
s.Lock()
defer s.Unlock()
return len(s.m)
}

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