Add spoke operator

This commit is contained in:
Jian Qiu
2020-04-30 14:14:17 +08:00
parent fdbe918b1b
commit ce565e6380
24 changed files with 1598 additions and 125 deletions

View File

@@ -11,12 +11,13 @@ include $(addprefix ./vendor/github.com/openshift/build-machinery-go/make/, \
)
$(call add-bindata,hub,./manifests/hub/...,bindata,bindata,./pkg/operators/hub/bindata/bindata.go)
$(call add-bindata,spoke,./manifests/spoke/...,bindata,bindata,./pkg/operators/spoke/bindata/bindata.go)
copy-crd:
cp ./vendor/github.com/open-cluster-management/api/cluster/v1/*.yaml ./manifests/hub/
cp ./vendor/github.com/open-cluster-management/api/work/v1/*.yaml ./manifests/hub/
update-all: copy-crd update
update-all: copy-crd update-bindata-hub update-bindata-spoke
clean:
$(RM) ./nucleus

2
go.mod
View File

@@ -4,7 +4,7 @@ go 1.13
require (
github.com/jteeuwen/go-bindata v3.0.8-0.20151023091102-a0ff2567cfb7+incompatible
github.com/open-cluster-management/api v0.0.0-20200506150956-355c9d6ef16b
github.com/open-cluster-management/api v0.0.0-20200512021938-5f0d5c12ea67
github.com/openshift/api v0.0.0-20200326160804-ecb9283fe820
github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160
github.com/openshift/library-go v0.0.0-20200414135834-ccc4bb27d032

4
go.sum
View File

@@ -279,8 +279,8 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/open-cluster-management/api v0.0.0-20200506150956-355c9d6ef16b h1:vaOPmEZRgtXSa7SBkyrZKR7HStXTAAv1kQoc/nlZ66g=
github.com/open-cluster-management/api v0.0.0-20200506150956-355c9d6ef16b/go.mod h1:RgKeB8PJ7upe2QXy/MpCejU/xm2G6/i0Y+/itWuPugs=
github.com/open-cluster-management/api v0.0.0-20200512021938-5f0d5c12ea67 h1:JHbmXcawfV/Flb+csFz+FFHOG9EfEfcUH5cggNX8Yu4=
github.com/open-cluster-management/api v0.0.0-20200512021938-5f0d5c12ea67/go.mod h1:RgKeB8PJ7upe2QXy/MpCejU/xm2G6/i0Y+/itWuPugs=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20191031171055-b133feaeeb2e/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=

View File

@@ -50,7 +50,11 @@ spec:
to be deployed on the spoke cluster.
type: array
items:
description: Manifest represents a resource to be deployed on
spoke cluster
type: object
x-kubernetes-preserve-unknown-fields: true
x-kubernetes-embedded-resource: true
status:
description: Status represents the current status of work
type: object

View File

@@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:open-cluster-management:{{ .SpokeCoreName }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: {{ .SpokeCoreName }}-sa
namespace: {{ .SpokeCoreNamespace }}

View File

@@ -0,0 +1,55 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: {{ .SpokeCoreName }}-registration-agent
namespace: {{ .SpokeCoreNamespace }}
labels:
app: spoke-registration-agent
spec:
replicas: 1
selector:
matchLabels:
app: spoke-registration-agent
template:
metadata:
labels:
app: spoke-registration-agent
spec:
serviceAccountName: {{ .SpokeCoreName }}-sa
containers:
- name: spoke-agent
image: {{ .RegistrationImage }}
imagePullPolicy: IfNotPresent
args:
- "/registration"
- "agent"
- "--cluster-name={{ .ClusterName }}"
- "--bootstrap-kubeconfig=/spoke/bootstrap/kubeconfig"
- "--spoke-external-server-url={{ .ExternalServerURL }}"
volumeMounts:
- name: bootstrap-secret
mountPath: "/spoke/bootstrap"
readOnly: true
- name: hub-kubeconfig-secret
mountPath: "/spoke/hub-kubeconfig"
readOnly: true
livenessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
volumes:
- name: bootstrap-secret
secret:
secretName: {{ .BootStrapKubeConfigSecret }}
- name: hub-kubeconfig-secret
secret:
secretName: {{ .HubKubeConfigSecret }}

View File

@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .SpokeCoreName }}-sa
namespace: {{ .SpokeCoreNamespace }}

View File

@@ -0,0 +1,48 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: {{ .SpokeCoreName }}-work-agent
namespace: {{ .SpokeCoreNamespace }}
labels:
app: spoke-work-agent
spec:
replicas: 3
selector:
matchLabels:
app: spoke-work-agent
template:
metadata:
labels:
app: spoke-work-agent
spec:
serviceAccountName: {{ .SpokeCoreName }}-sa
containers:
- name: spoke-agent
image: {{ .WorkImage }}
imagePullPolicy: IfNotPresent
args:
- "/work"
- "agent"
- "--cluster-name={{ .ClusterName }}"
- "--kubeconfig=/spoke/hub-kubeconfig"
volumeMounts:
- name: hub-kubeconfig-secret
mountPath: "/spoke/hub-kubeconfig"
readOnly: true
livenessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
volumes:
- name: hub-kubeconfig-secret
secret:
secretName: {{ .HubKubeConfigSecret }}

View File

@@ -9,10 +9,10 @@ import (
"github.com/open-cluster-management/nucleus/pkg/version"
)
// NewOperator generatee a command to start workload agent
// NewOperatorCmd generatee a command to start workload agent
func NewOperatorCmd() *cobra.Command {
cmd := controllercmd.
NewControllerCommandConfig("operator", version.Get(), operators.RunNucleusOperator).
NewControllerCommandConfig("nucleus-operator", version.Get(), operators.RunNucleusOperator).
NewCommand()
cmd.Use = "operator"
cmd.Short = "Start the nucleus operator"

View File

@@ -96,3 +96,53 @@ func UpdateNucleusHubConditionFn(conds ...nucleusapiv1.StatusCondition) UpdateNu
return nil
}
}
type UpdateNucleusSpokeStatusFunc func(status *nucleusapiv1.SpokeCoreStatus) error
func UpdateNucleusSpokeStatus(
ctx context.Context,
client nucleusv1client.SpokeCoreInterface,
nucleusSpokeCoreName string,
updateFuncs ...UpdateNucleusSpokeStatusFunc) (*nucleusapiv1.SpokeCoreStatus, bool, error) {
updated := false
var updatedSpokeClusterStatus *nucleusapiv1.SpokeCoreStatus
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
spokeCore, err := client.Get(ctx, nucleusSpokeCoreName, metav1.GetOptions{})
if err != nil {
return err
}
oldStatus := &spokeCore.Status
newStatus := oldStatus.DeepCopy()
for _, update := range updateFuncs {
if err := update(newStatus); err != nil {
return err
}
}
if equality.Semantic.DeepEqual(oldStatus, newStatus) {
// We return the newStatus which is a deep copy of oldStatus but with all update funcs applied.
updatedSpokeClusterStatus = newStatus
return nil
}
spokeCore.Status = *newStatus
updatedSpokeCluster, err := client.UpdateStatus(ctx, spokeCore, metav1.UpdateOptions{})
if err != nil {
return err
}
updatedSpokeClusterStatus = &updatedSpokeCluster.Status
updated = err == nil
return err
})
return updatedSpokeClusterStatus, updated, err
}
func UpdateNucleusSpokeConditionFn(conds ...nucleusapiv1.StatusCondition) UpdateNucleusSpokeStatusFunc {
return func(oldStatus *nucleusapiv1.SpokeCoreStatus) error {
for _, cond := range conds {
SetNucleusCondition(&oldStatus.Conditions, cond)
}
return nil
}
}

View File

@@ -73,14 +73,22 @@ func TestUpdateStatusCondition(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
fakeClusterClient := nucleusfake.NewSimpleClientset(&nucleusapiv1.HubCore{
ObjectMeta: metav1.ObjectMeta{Name: "testspokecluster"},
Status: nucleusapiv1.HubCoreStatus{
Conditions: c.startingConditions,
fakeClusterClient := nucleusfake.NewSimpleClientset(
&nucleusapiv1.HubCore{
ObjectMeta: metav1.ObjectMeta{Name: "testspokecluster"},
Status: nucleusapiv1.HubCoreStatus{
Conditions: c.startingConditions,
},
},
})
&nucleusapiv1.SpokeCore{
ObjectMeta: metav1.ObjectMeta{Name: "testspokecluster"},
Status: nucleusapiv1.SpokeCoreStatus{
Conditions: c.startingConditions,
},
},
)
status, updated, err := UpdateNucleusHubStatus(
hubstatus, updated, err := UpdateNucleusHubStatus(
context.TODO(),
fakeClusterClient.NucleusV1().HubCores(),
"testspokecluster",
@@ -92,14 +100,36 @@ func TestUpdateStatusCondition(t *testing.T) {
if updated != c.expextedUpdated {
t.Errorf("expected %t, but %t", c.expextedUpdated, updated)
}
spokestatus, updated, err := UpdateNucleusSpokeStatus(
context.TODO(),
fakeClusterClient.NucleusV1().SpokeCores(),
"testspokecluster",
UpdateNucleusSpokeConditionFn(c.newCondition),
)
if err != nil {
t.Errorf("unexpected err: %v", err)
}
if updated != c.expextedUpdated {
t.Errorf("expected %t, but %t", c.expextedUpdated, updated)
}
for i := range c.expectedConditions {
expected := c.expectedConditions[i]
actual := status.Conditions[i]
hubactual := hubstatus.Conditions[i]
if expected.LastTransitionTime == (metav1.Time{}) {
actual.LastTransitionTime = metav1.Time{}
hubactual.LastTransitionTime = metav1.Time{}
}
if !equality.Semantic.DeepEqual(expected, actual) {
t.Errorf(diff.ObjectDiff(expected, actual))
if !equality.Semantic.DeepEqual(expected, hubactual) {
t.Errorf(diff.ObjectDiff(expected, hubactual))
}
spokeactual := spokestatus.Conditions[i]
if expected.LastTransitionTime == (metav1.Time{}) {
spokeactual.LastTransitionTime = metav1.Time{}
}
if !equality.Semantic.DeepEqual(expected, spokeactual) {
t.Errorf(diff.ObjectDiff(expected, spokeactual))
}
}
})

View File

@@ -1,7 +1,7 @@
// Code generated by go-bindata.
// sources:
// manifests/hub/0000_00_clusters.open-cluster-management.io_spokeclusters.crd.yaml
// manifests/hub/0000_00_work.open-cluster-management.io_workloads.crd.yaml
// manifests/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml
// manifests/hub/hub-clusterrole.yaml
// manifests/hub/hub-clusterrolebinding.yaml
// manifests/hub/hub-deployment.yaml
@@ -201,7 +201,7 @@ func manifestsHub0000_00_clustersOpenClusterManagementIo_spokeclustersCrdYaml()
return a, nil
}
var _manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYaml = []byte(`apiVersion: apiextensions.k8s.io/v1beta1
var _manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYaml = []byte(`apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
@@ -253,7 +253,11 @@ spec:
to be deployed on the spoke cluster.
type: array
items:
description: Manifest represents a resource to be deployed on
spoke cluster
type: object
x-kubernetes-preserve-unknown-fields: true
x-kubernetes-embedded-resource: true
status:
description: Status represents the current status of work
type: object
@@ -385,17 +389,17 @@ status:
storedVersions: []
`)
func manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYamlBytes() ([]byte, error) {
return _manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYaml, nil
func manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYamlBytes() ([]byte, error) {
return _manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYaml, nil
}
func manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYaml() (*asset, error) {
bytes, err := manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYamlBytes()
func manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYaml() (*asset, error) {
bytes, err := manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYamlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "manifests/hub/0000_00_work.open-cluster-management.io_workloads.crd.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
info := bindataFileInfo{name: "manifests/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@@ -627,7 +631,7 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"manifests/hub/0000_00_clusters.open-cluster-management.io_spokeclusters.crd.yaml": manifestsHub0000_00_clustersOpenClusterManagementIo_spokeclustersCrdYaml,
"manifests/hub/0000_00_work.open-cluster-management.io_workloads.crd.yaml": manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYaml,
"manifests/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml": manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYaml,
"manifests/hub/hub-clusterrole.yaml": manifestsHubHubClusterroleYaml,
"manifests/hub/hub-clusterrolebinding.yaml": manifestsHubHubClusterrolebindingYaml,
"manifests/hub/hub-deployment.yaml": manifestsHubHubDeploymentYaml,
@@ -679,7 +683,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
"manifests": {nil, map[string]*bintree{
"hub": {nil, map[string]*bintree{
"0000_00_clusters.open-cluster-management.io_spokeclusters.crd.yaml": {manifestsHub0000_00_clustersOpenClusterManagementIo_spokeclustersCrdYaml, map[string]*bintree{}},
"0000_00_work.open-cluster-management.io_workloads.crd.yaml": {manifestsHub0000_00_workOpenClusterManagementIo_workloadsCrdYaml, map[string]*bintree{}},
"0000_00_work.open-cluster-management.io_manifestworks.crd.yaml": {manifestsHub0000_00_workOpenClusterManagementIo_manifestworksCrdYaml, map[string]*bintree{}},
"hub-clusterrole.yaml": {manifestsHubHubClusterroleYaml, map[string]*bintree{}},
"hub-clusterrolebinding.yaml": {manifestsHubHubClusterrolebindingYaml, map[string]*bintree{}},
"hub-deployment.yaml": {manifestsHubHubDeploymentYaml, map[string]*bintree{}},

View File

@@ -9,6 +9,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
@@ -77,7 +78,10 @@ func NewNucleusHubController(
return factory.New().WithSync(controller.sync).
ResyncEvery(3*time.Minute).
WithInformers(nucleusInformer.Informer()).
WithInformersQueueKeyFunc(func(obj runtime.Object) string {
accessor, _ := meta.Accessor(obj)
return accessor.GetName()
}, nucleusInformer.Informer()).
ToController("NucleusHubController", recorder)
}
@@ -93,6 +97,10 @@ func (n *nucleusHubController) sync(ctx context.Context, controllerContext facto
klog.V(4).Infof("Reconciling HubCore %q", hubCoreName)
hubCore, err := n.nucleusLister.Get(hubCoreName)
if errors.IsNotFound(err) {
// HubCore not found, could have been deleted, do nothing.
return nil
}
if err != nil {
return err
}
@@ -137,7 +145,7 @@ func (n *nucleusHubController) sync(ctx context.Context, controllerContext facto
return assets.MustCreateAssetFromTemplate(name, bindata.MustAsset(filepath.Join("", name)), config).Data, nil
},
"manifests/hub/0000_00_clusters.open-cluster-management.io_spokeclusters.crd.yaml",
"manifests/hub/0000_00_work.open-cluster-management.io_workloads.crd.yaml",
"manifests/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml",
"manifests/hub/hub-clusterrole.yaml",
"manifests/hub/hub-clusterrolebinding.yaml",
"manifests/hub/hub-namespace.yaml",

View File

@@ -40,11 +40,15 @@ func RunNucleusOperator(ctx context.Context, controllerContext *controllercmd.Co
nucleusClient.NucleusV1().HubCores(),
nucleusInformer.Nucleus().V1().HubCores(),
controllerContext.EventRecorder)
agentController := spoke.NewNucleusAgentController(kubeClient, controllerContext.EventRecorder)
spokeController := spoke.NewNucleusSpokeController(
kubeClient,
nucleusClient.NucleusV1().SpokeCores(),
nucleusInformer.Nucleus().V1().SpokeCores(),
controllerContext.EventRecorder)
go nucleusInformer.Start(ctx.Done())
go hubcontroller.Run(ctx, 1)
go agentController.Run(ctx, 1)
go spokeController.Run(ctx, 1)
<-ctx.Done()
return nil
}

View File

@@ -0,0 +1,393 @@
// Code generated by go-bindata.
// sources:
// manifests/spoke/spoke-clusterrolebinding.yaml
// manifests/spoke/spoke-registration-deployment.yaml
// manifests/spoke/spoke-serviceaccount.yaml
// manifests/spoke/spoke-work-deployment.yaml
// DO NOT EDIT!
package bindata
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
type asset struct {
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindataFileInfo) Name() string {
return fi.name
}
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
func (fi bindataFileInfo) IsDir() bool {
return false
}
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
var _manifestsSpokeSpokeClusterrolebindingYaml = []byte(`apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:open-cluster-management:{{ .SpokeCoreName }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: {{ .SpokeCoreName }}-sa
namespace: {{ .SpokeCoreNamespace }}
`)
func manifestsSpokeSpokeClusterrolebindingYamlBytes() ([]byte, error) {
return _manifestsSpokeSpokeClusterrolebindingYaml, nil
}
func manifestsSpokeSpokeClusterrolebindingYaml() (*asset, error) {
bytes, err := manifestsSpokeSpokeClusterrolebindingYamlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "manifests/spoke/spoke-clusterrolebinding.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _manifestsSpokeSpokeRegistrationDeploymentYaml = []byte(`kind: Deployment
apiVersion: apps/v1
metadata:
name: {{ .SpokeCoreName }}-registration-agent
namespace: {{ .SpokeCoreNamespace }}
labels:
app: spoke-registration-agent
spec:
replicas: 1
selector:
matchLabels:
app: spoke-registration-agent
template:
metadata:
labels:
app: spoke-registration-agent
spec:
serviceAccountName: {{ .SpokeCoreName }}-sa
containers:
- name: spoke-agent
image: {{ .RegistrationImage }}
imagePullPolicy: IfNotPresent
args:
- "/registration"
- "agent"
- "--cluster-name={{ .ClusterName }}"
- "--bootstrap-kubeconfig=/spoke/bootstrap/kubeconfig"
- "--spoke-external-server-url={{ .ExternalServerURL }}"
volumeMounts:
- name: bootstrap-secret
mountPath: "/spoke/bootstrap"
readOnly: true
- name: hub-kubeconfig-secret
mountPath: "/spoke/hub-kubeconfig"
readOnly: true
livenessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
volumes:
- name: bootstrap-secret
secret:
secretName: {{ .BootStrapKubeConfigSecret }}
- name: hub-kubeconfig-secret
secret:
secretName: {{ .HubKubeConfigSecret }}
`)
func manifestsSpokeSpokeRegistrationDeploymentYamlBytes() ([]byte, error) {
return _manifestsSpokeSpokeRegistrationDeploymentYaml, nil
}
func manifestsSpokeSpokeRegistrationDeploymentYaml() (*asset, error) {
bytes, err := manifestsSpokeSpokeRegistrationDeploymentYamlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "manifests/spoke/spoke-registration-deployment.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _manifestsSpokeSpokeServiceaccountYaml = []byte(`apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .SpokeCoreName }}-sa
namespace: {{ .SpokeCoreNamespace }}
`)
func manifestsSpokeSpokeServiceaccountYamlBytes() ([]byte, error) {
return _manifestsSpokeSpokeServiceaccountYaml, nil
}
func manifestsSpokeSpokeServiceaccountYaml() (*asset, error) {
bytes, err := manifestsSpokeSpokeServiceaccountYamlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "manifests/spoke/spoke-serviceaccount.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var _manifestsSpokeSpokeWorkDeploymentYaml = []byte(`kind: Deployment
apiVersion: apps/v1
metadata:
name: {{ .SpokeCoreName }}-work-agent
namespace: {{ .SpokeCoreNamespace }}
labels:
app: spoke-work-agent
spec:
replicas: 3
selector:
matchLabels:
app: spoke-work-agent
template:
metadata:
labels:
app: spoke-work-agent
spec:
serviceAccountName: {{ .SpokeCoreName }}-sa
containers:
- name: spoke-agent
image: {{ .WorkImage }}
imagePullPolicy: IfNotPresent
args:
- "/work"
- "agent"
- "--cluster-name={{ .ClusterName }}"
- "--kubeconfig=/spoke/hub-kubeconfig"
volumeMounts:
- name: hub-kubeconfig-secret
mountPath: "/spoke/hub-kubeconfig"
readOnly: true
livenessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
scheme: HTTPS
port: 443
initialDelaySeconds: 2
volumes:
- name: hub-kubeconfig-secret
secret:
secretName: {{ .HubKubeConfigSecret }}
`)
func manifestsSpokeSpokeWorkDeploymentYamlBytes() ([]byte, error) {
return _manifestsSpokeSpokeWorkDeploymentYaml, nil
}
func manifestsSpokeSpokeWorkDeploymentYaml() (*asset, error) {
bytes, err := manifestsSpokeSpokeWorkDeploymentYamlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "manifests/spoke/spoke-work-deployment.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
// Asset loads and returns the asset for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
}
return a.bytes, nil
}
return nil, fmt.Errorf("Asset %s not found", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if err != nil {
panic("asset: Asset(" + name + "): " + err.Error())
}
return a
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
}
return a.info, nil
}
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
for name := range _bindata {
names = append(names, name)
}
return names
}
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"manifests/spoke/spoke-clusterrolebinding.yaml": manifestsSpokeSpokeClusterrolebindingYaml,
"manifests/spoke/spoke-registration-deployment.yaml": manifestsSpokeSpokeRegistrationDeploymentYaml,
"manifests/spoke/spoke-serviceaccount.yaml": manifestsSpokeSpokeServiceaccountYaml,
"manifests/spoke/spoke-work-deployment.yaml": manifestsSpokeSpokeWorkDeploymentYaml,
}
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
// following hierarchy:
// data/
// foo.txt
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
}
}
if node.Func != nil {
return nil, fmt.Errorf("Asset %s not found", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
rv = append(rv, childName)
}
return rv, nil
}
type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"manifests": {nil, map[string]*bintree{
"spoke": {nil, map[string]*bintree{
"spoke-clusterrolebinding.yaml": {manifestsSpokeSpokeClusterrolebindingYaml, map[string]*bintree{}},
"spoke-registration-deployment.yaml": {manifestsSpokeSpokeRegistrationDeploymentYaml, map[string]*bintree{}},
"spoke-serviceaccount.yaml": {manifestsSpokeSpokeServiceaccountYaml, map[string]*bintree{}},
"spoke-work-deployment.yaml": {manifestsSpokeSpokeWorkDeploymentYaml, map[string]*bintree{}},
}},
}},
}}
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@@ -2,28 +2,395 @@ package spoke
import (
"context"
"fmt"
"path/filepath"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"github.com/openshift/api"
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/events"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"github.com/openshift/library-go/pkg/assets"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
operatorhelpers "github.com/openshift/library-go/pkg/operator/v1helpers"
nucleusv1client "github.com/open-cluster-management/api/client/nucleus/clientset/versioned/typed/nucleus/v1"
nucleusinformer "github.com/open-cluster-management/api/client/nucleus/informers/externalversions/nucleus/v1"
nucleuslister "github.com/open-cluster-management/api/client/nucleus/listers/nucleus/v1"
nucleusapiv1 "github.com/open-cluster-management/api/nucleus/v1"
"github.com/open-cluster-management/nucleus/pkg/helpers"
"github.com/open-cluster-management/nucleus/pkg/operators/spoke/bindata"
)
type nucleusAgentController struct {
kubeClient kubernetes.Interface
const (
nucleusSpokeFinalizer = "nucleus.open-cluster-management.io/spoke-core-cleanup"
bootstrapHubKubeConfigSecret = "bootstrap-hub-kubeconfig"
hubKubeConfigSecret = "hub-kubeconfig"
nucluesSpokeCoreNamespace = "open-cluster-management"
spokeCoreApplied = "Applied"
)
var (
genericScheme = runtime.NewScheme()
genericCodecs = serializer.NewCodecFactory(genericScheme)
genericCodec = genericCodecs.UniversalDeserializer()
)
type nucleusSpokeController struct {
nucleusClient nucleusv1client.SpokeCoreInterface
nucleusLister nucleuslister.SpokeCoreLister
kubeClient kubernetes.Interface
registrationGeneration int64
workGeneration int64
}
// NewNucleusAgentController construct nucleus agent controller
func NewNucleusAgentController(
func init() {
utilruntime.Must(api.InstallKube(genericScheme))
}
// NewNucleusSpokeController construct nucleus spoke controller
func NewNucleusSpokeController(
kubeClient kubernetes.Interface,
nucleusClient nucleusv1client.SpokeCoreInterface,
nucleusInformer nucleusinformer.SpokeCoreInformer,
recorder events.Recorder) factory.Controller {
controller := &nucleusAgentController{
kubeClient: kubeClient,
controller := &nucleusSpokeController{
kubeClient: kubeClient,
nucleusClient: nucleusClient,
nucleusLister: nucleusInformer.Lister(),
}
return factory.New().WithSync(controller.sync).ToController("NucleusHubController", recorder)
return factory.New().WithSync(controller.sync).
WithInformersQueueKeyFunc(func(obj runtime.Object) string {
accessor, _ := meta.Accessor(obj)
return accessor.GetName()
}, nucleusInformer.Informer()).
ToController("NucleusSpokeController", recorder)
}
func (m *nucleusAgentController) sync(ctx context.Context, controllerContext factory.SyncContext) error {
// spokeConfig is used to render the template of hub manifests
type spokeConfig struct {
SpokeCoreName string
SpokeCoreNamespace string
RegistrationImage string
WorkImage string
ClusterName string
ExternalServerURL string
HubKubeConfigSecret string
BootStrapKubeConfigSecret string
}
func (n *nucleusSpokeController) sync(ctx context.Context, controllerContext factory.SyncContext) error {
spokeCoreName := controllerContext.QueueKey()
klog.V(4).Infof("Reconciling SpokeCore %q", spokeCoreName)
spokeCore, err := n.nucleusLister.Get(spokeCoreName)
if errors.IsNotFound(err) {
// AgentCore not found, could have been deleted, do nothing.
return nil
}
if err != nil {
return err
}
spokeCore = spokeCore.DeepCopy()
config := spokeConfig{
SpokeCoreName: spokeCore.Name,
SpokeCoreNamespace: spokeCore.Spec.Namespace,
RegistrationImage: spokeCore.Spec.RegistrationImagePullSpec,
WorkImage: spokeCore.Spec.WorkImagePullSpec,
ClusterName: spokeCore.Spec.ClusterName,
BootStrapKubeConfigSecret: bootstrapHubKubeConfigSecret,
HubKubeConfigSecret: hubKubeConfigSecret,
}
// If namespace is not set, use the default namespace
if config.SpokeCoreNamespace == "" {
config.SpokeCoreNamespace = nucluesSpokeCoreNamespace
}
// Update finalizer at first
if spokeCore.DeletionTimestamp.IsZero() {
hasFinalizer := false
for i := range spokeCore.Finalizers {
if spokeCore.Finalizers[i] == nucleusSpokeFinalizer {
hasFinalizer = true
break
}
}
if !hasFinalizer {
spokeCore.Finalizers = append(spokeCore.Finalizers, nucleusSpokeFinalizer)
_, err := n.nucleusClient.Update(ctx, spokeCore, metav1.UpdateOptions{})
return err
}
}
// SpokeCore is deleting, we remove its related resources on spoke
if !spokeCore.DeletionTimestamp.IsZero() {
if err := n.cleanUp(ctx, controllerContext, config); err != nil {
return err
}
return n.removeWorkFinalizer(ctx, spokeCore)
}
// Start deploy spoke core components
// Check if namespace exists
_, err = n.kubeClient.CoreV1().Namespaces().Get(ctx, config.SpokeCoreNamespace, metav1.GetOptions{})
if err != nil {
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: fmt.Sprintf("Failed to get namespace %q", config.SpokeCoreNamespace),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return err
}
// Check if bootstrap secret exists
_, err = n.kubeClient.CoreV1().Secrets(config.SpokeCoreNamespace).Get(
ctx, config.BootStrapKubeConfigSecret, metav1.GetOptions{})
if err != nil {
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: fmt.Sprintf("Failed to get bootstracp secret %s/%s", config.SpokeCoreNamespace, config.BootStrapKubeConfigSecret),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return err
}
// Deploy the static resources
// Apply static files
resourceResults := resourceapply.ApplyDirectly(
resourceapply.NewKubeClientHolder(n.kubeClient),
controllerContext.Recorder(),
func(name string) ([]byte, error) {
return assets.MustCreateAssetFromTemplate(name, bindata.MustAsset(filepath.Join("", name)), config).Data, nil
},
"manifests/spoke/spoke-clusterrolebinding.yaml",
"manifests/spoke/spoke-serviceaccount.yaml",
)
errs := []error{}
for _, result := range resourceResults {
if result.Error != nil {
errs = append(errs, fmt.Errorf("%q (%T): %v", result.File, result.Type, result.Error))
}
}
if len(errs) > 0 {
appleErros := operatorhelpers.NewMultiLineAggregate(errs)
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: appleErros.Error(),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return appleErros
}
// Create hub config secret
hubSecret, err := n.kubeClient.CoreV1().Secrets(config.SpokeCoreNamespace).Get(
ctx, hubKubeConfigSecret, metav1.GetOptions{})
if errors.IsNotFound(err) {
// Craete an empty secret with placeholder
hubSecret = &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: hubKubeConfigSecret,
Namespace: config.SpokeCoreNamespace,
},
Data: map[string][]byte{"placeholder": []byte("placeholder")},
}
hubSecret, err = n.kubeClient.CoreV1().Secrets(config.SpokeCoreNamespace).Create(ctx, hubSecret, metav1.CreateOptions{})
if err != nil {
return err
}
}
if err != nil {
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: fmt.Sprintf("Failed to get hub kubeconfig secret with error %v", err),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return err
}
// Deploy registration agent
generation, err := n.applyDeployment(
config, "manifests/spoke/spoke-registration-deployment.yaml", n.registrationGeneration, controllerContext)
if err != nil {
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: fmt.Sprintf("Failed to deploy registration deployment with error %v", err),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return err
}
n.registrationGeneration = generation
// If cluster name is empty, read cluster name from hub config secret
if config.ClusterName == "" {
clusterName := hubSecret.Data["cluster-name"]
if clusterName == nil {
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: fmt.Sprintf("Failed to get cluster name"),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return fmt.Errorf("Failed to get cluster name")
}
config.ClusterName = string(clusterName)
}
// If hub kubeconfig does not exist, return err.
if hubSecret.Data["kubeconfig"] == nil {
klog.Infof("data is %#v", hubSecret.Data)
return fmt.Errorf("Failed to get kubeconfig from hub kubeconfig secret")
}
// Deploy work agent
generation, err = n.applyDeployment(
config, "manifests/spoke/spoke-work-deployment.yaml", n.workGeneration, controllerContext)
if err != nil {
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionFalse,
Reason: "SpokeCoreApplyFailed",
Message: fmt.Sprintf("Failed to deploy work deployment with error %v", err),
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return err
}
n.workGeneration = generation
// Update status
helpers.SetNucleusCondition(&spokeCore.Status.Conditions, nucleusapiv1.StatusCondition{
Type: spokeCoreApplied,
Status: metav1.ConditionTrue,
Reason: "SpokeCoreApplied",
Message: "Spoke Core Component Applied",
})
helpers.UpdateNucleusSpokeStatus(
ctx, n.nucleusClient, spokeCoreName, helpers.UpdateNucleusSpokeConditionFn(spokeCore.Status.Conditions...))
return err
}
func (n *nucleusSpokeController) applyDeployment(
config spokeConfig, file string, generation int64, controllerContext factory.SyncContext) (int64, error) {
deploymentRaw := assets.MustCreateAssetFromTemplate(
file,
bindata.MustAsset(filepath.Join("", file)), config).Data
deployment, _, err := genericCodec.Decode(deploymentRaw, nil, nil)
if err != nil {
return 0, fmt.Errorf("%q: %v", file, err)
}
updatedDeployment, updated, err := resourceapply.ApplyDeployment(
n.kubeClient.AppsV1(),
controllerContext.Recorder(),
deployment.(*appsv1.Deployment), generation, false)
if err != nil {
klog.Errorf("Failed to apply hub deployment manifest: %v", err)
return 0, fmt.Errorf("%q (%T): %v", file, deployment, err)
}
// Record the generation, so the deployment is only updated when generation is changed.
if updated {
generation = updatedDeployment.ObjectMeta.Generation
}
return generation, nil
}
func (n *nucleusSpokeController) cleanUp(ctx context.Context, controllerContext factory.SyncContext, config spokeConfig) error {
// Remove deployment
registrationDeployment := fmt.Sprintf("%s-registration-agent", config.SpokeCoreName)
err := n.kubeClient.AppsV1().Deployments(config.SpokeCoreNamespace).Delete(ctx, registrationDeployment, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return err
}
controllerContext.Recorder().Eventf("DeploymentDeleted", "deployment %s is deleted", registrationDeployment)
workDeployment := fmt.Sprintf("%s-work-agent", config.SpokeCoreName)
err = n.kubeClient.AppsV1().Deployments(config.SpokeCoreNamespace).Delete(ctx, workDeployment, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return err
}
// Remove secret
err = n.kubeClient.CoreV1().Secrets(config.SpokeCoreNamespace).Delete(ctx, config.HubKubeConfigSecret, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return err
}
controllerContext.Recorder().Eventf("SecretDeleted", "secret %s is deleted", config.HubKubeConfigSecret)
// Remove service account
serviceAccountName := fmt.Sprintf("%s-sa", config.SpokeCoreName)
err = n.kubeClient.CoreV1().ServiceAccounts(config.SpokeCoreNamespace).Delete(ctx, serviceAccountName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return err
}
controllerContext.Recorder().Eventf("ServiceAccountDeleted", "serviceaccoount %s is deleted", serviceAccountName)
// Remove clusterrolebinding
clusterRoleBindingName := fmt.Sprintf("system:open-cluster-management:%s", config.SpokeCoreName)
err = n.kubeClient.RbacV1().ClusterRoleBindings().Delete(ctx, clusterRoleBindingName, metav1.DeleteOptions{})
if err != nil && !errors.IsNotFound(err) {
return err
}
controllerContext.Recorder().Eventf("ClusterRoleBindingDeleted", "clusterrole %s is deleted", clusterRoleBindingName)
return nil
}
func (n *nucleusSpokeController) removeWorkFinalizer(ctx context.Context, deploy *nucleusapiv1.SpokeCore) error {
copiedFinalizers := []string{}
for i := range deploy.Finalizers {
if deploy.Finalizers[i] == nucleusSpokeFinalizer {
continue
}
copiedFinalizers = append(copiedFinalizers, deploy.Finalizers[i])
}
if len(deploy.Finalizers) != len(copiedFinalizers) {
deploy.Finalizers = copiedFinalizers
_, err := n.nucleusClient.Update(ctx, deploy, metav1.UpdateOptions{})
return err
}
return nil
}
func readClusterNameFromSecret(secret *corev1.Secret) (string, error) {
if secret.Data["cluster-name"] == nil {
return "", fmt.Errorf("Unable to find cluster name in secret")
}
return string(secret.Data["cluster-name"]), nil
}
func readKubuConfigFromSecret(secret *corev1.Secret, config spokeConfig) (string, error) {
if secret.Data["cluster-name"] == nil {
return "", fmt.Errorf("Unable to find cluster name in secret")
}
return string(secret.Data["cluster-name"]), nil
}

View File

@@ -0,0 +1,279 @@
package spoke
import (
"fmt"
"strings"
"testing"
"time"
fakenucleusclient "github.com/open-cluster-management/api/client/nucleus/clientset/versioned/fake"
nucleusinformers "github.com/open-cluster-management/api/client/nucleus/informers/externalversions"
nucleusapiv1 "github.com/open-cluster-management/api/nucleus/v1"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/events/eventstesting"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
fakekube "k8s.io/client-go/kubernetes/fake"
clienttesting "k8s.io/client-go/testing"
"k8s.io/client-go/util/workqueue"
)
type testController struct {
controller *nucleusSpokeController
kubeClient *fakekube.Clientset
nucleusClient *fakenucleusclient.Clientset
}
type fakeSyncContext struct {
key string
queue workqueue.RateLimitingInterface
recorder events.Recorder
}
func (f fakeSyncContext) Queue() workqueue.RateLimitingInterface { return f.queue }
func (f fakeSyncContext) QueueKey() string { return f.key }
func (f fakeSyncContext) Recorder() events.Recorder { return f.recorder }
func newFakeSyncContext(t *testing.T, key string) *fakeSyncContext {
return &fakeSyncContext{
key: key,
queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
recorder: eventstesting.NewTestingEventRecorder(t),
}
}
func newSpokeCore(name, namespace, clustername string) *nucleusapiv1.SpokeCore {
return &nucleusapiv1.SpokeCore{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Finalizers: []string{nucleusSpokeFinalizer},
},
Spec: nucleusapiv1.SpokeCoreSpec{
RegistrationImagePullSpec: "testregistration",
WorkImagePullSpec: "testwork",
ClusterName: clustername,
Namespace: namespace,
},
}
}
func newSecret(name, namespace string) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Data: map[string][]byte{},
}
}
func newNamespace(name string) *corev1.Namespace {
return &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
}
}
func newTestController(spokecore *nucleusapiv1.SpokeCore, objects ...runtime.Object) *testController {
fakeKubeClient := fakekube.NewSimpleClientset(objects...)
fakeNucleusClient := fakenucleusclient.NewSimpleClientset(spokecore)
nucleusInformers := nucleusinformers.NewSharedInformerFactory(fakeNucleusClient, 5*time.Minute)
hubController := &nucleusSpokeController{
nucleusClient: fakeNucleusClient.NucleusV1().SpokeCores(),
kubeClient: fakeKubeClient,
nucleusLister: nucleusInformers.Nucleus().V1().SpokeCores().Lister(),
}
store := nucleusInformers.Nucleus().V1().SpokeCores().Informer().GetStore()
store.Add(spokecore)
return &testController{
controller: hubController,
kubeClient: fakeKubeClient,
nucleusClient: fakeNucleusClient,
}
}
func assertAction(t *testing.T, actual clienttesting.Action, expected string) {
if actual.GetVerb() != expected {
t.Errorf("expected %s action but got: %#v", expected, actual)
}
}
func assertCondition(t *testing.T, actual runtime.Object, expectedCondition string, expectedStatus metav1.ConditionStatus) {
spokeCore := actual.(*nucleusapiv1.SpokeCore)
conditions := spokeCore.Status.Conditions
if len(conditions) != 1 {
t.Errorf("expected 1 condition but got: %#v", conditions)
}
condition := conditions[0]
if condition.Type != expectedCondition {
t.Errorf("expected %s but got: %s", expectedCondition, condition.Type)
}
if condition.Status != expectedStatus {
t.Errorf("expected %s but got: %s", expectedStatus, condition.Status)
}
}
func ensureNameNamespace(t *testing.T, actualName, actualNamespace, name, namespace string) {
if actualName != name {
t.Errorf("Name of the object does not match, expected %s, actual %s", name, actualName)
}
if actualNamespace != namespace {
t.Errorf("Namespace of the object does not match, expected %s, actual %s", namespace, actualNamespace)
}
}
func ensureObject(t *testing.T, object runtime.Object, spokeCore *nucleusapiv1.SpokeCore) {
access, err := meta.Accessor(object)
if err != nil {
t.Errorf("Unable to access objectmeta: %v", err)
}
switch o := object.(type) {
case *corev1.ServiceAccount:
ensureNameNamespace(t, access.GetName(), access.GetNamespace(), fmt.Sprintf("%s-sa", spokeCore.Name), spokeCore.Spec.Namespace)
case *appsv1.Deployment:
if strings.Contains(access.GetName(), "registration") {
ensureNameNamespace(
t, access.GetName(), access.GetNamespace(),
fmt.Sprintf("%s-registration-agent", spokeCore.Name), spokeCore.Spec.Namespace)
if spokeCore.Spec.RegistrationImagePullSpec != o.Spec.Template.Spec.Containers[0].Image {
t.Errorf("Image does not match to the expected.")
}
} else if strings.Contains(access.GetName(), "work") {
ensureNameNamespace(
t, access.GetName(), access.GetNamespace(),
fmt.Sprintf("%s-work-agent", spokeCore.Name), spokeCore.Spec.Namespace)
if spokeCore.Spec.WorkImagePullSpec != o.Spec.Template.Spec.Containers[0].Image {
t.Errorf("Image does not match to the expected.")
}
} else {
t.Errorf("Unexpected deployment")
}
}
}
// TestSyncDeploy test deployment of spoke components
func TestSyncDeploy(t *testing.T) {
spokeCore := newSpokeCore("testspoke", "testns", "cluster1")
bootStrapSecret := newSecret(bootstrapHubKubeConfigSecret, "testns")
hubKubeConfigSecret := newSecret(hubKubeConfigSecret, "testns")
hubKubeConfigSecret.Data["kubeconfig"] = []byte("dummuykubeconnfig")
namespace := newNamespace("testns")
controller := newTestController(spokeCore, bootStrapSecret, hubKubeConfigSecret, namespace)
syncContext := newFakeSyncContext(t, "testspoke")
err := controller.controller.sync(nil, syncContext)
if err != nil {
t.Errorf("Expected non error when sync, %v", err)
}
createObjects := []runtime.Object{}
kubeActions := controller.kubeClient.Actions()
for _, action := range kubeActions {
if action.GetVerb() == "create" {
object := action.(clienttesting.CreateActionImpl).Object
createObjects = append(createObjects, object)
}
}
// Check if resources are created as expected
if len(createObjects) != 4 {
t.Errorf("Expect 4 objects created in the sync loop, actual %d", len(createObjects))
}
for _, object := range createObjects {
ensureObject(t, object, spokeCore)
}
nucleusAction := controller.nucleusClient.Actions()
if len(nucleusAction) != 2 {
t.Errorf("Expect 2 actions in the sync loop, actual %#v", nucleusAction)
}
assertAction(t, nucleusAction[1], "update")
assertCondition(t, nucleusAction[1].(clienttesting.UpdateActionImpl).Object, spokeCoreApplied, metav1.ConditionTrue)
}
// TestSyncWithNoSecret test the scenario that bootstrap secret and hub config secret does not exist
func TestSyncWithNoSecret(t *testing.T) {
spokeCore := newSpokeCore("testspoke", "testns", "")
bootStrapSecret := newSecret(bootstrapHubKubeConfigSecret, "testns")
hubSecret := newSecret(hubKubeConfigSecret, "testns")
namespace := newNamespace("testns")
controller := newTestController(spokeCore, namespace)
syncContext := newFakeSyncContext(t, "testspoke")
// Return err since bootstrap secret does not exist
err := controller.controller.sync(nil, syncContext)
if err == nil {
t.Errorf("Expected error when sync")
}
nucleusAction := controller.nucleusClient.Actions()
if len(nucleusAction) != 2 {
t.Errorf("Expect 2 actions in the sync loop, actual %#v", nucleusAction)
}
assertAction(t, nucleusAction[1], "update")
assertCondition(t, nucleusAction[1].(clienttesting.UpdateActionImpl).Object, spokeCoreApplied, metav1.ConditionFalse)
// Add bootstrap secret and sync again
controller.kubeClient.PrependReactor("get", "secrets", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
if action.GetVerb() != "get" {
return false, nil, nil
}
getAction := action.(clienttesting.GetActionImpl)
if getAction.Name != bootstrapHubKubeConfigSecret {
return false, nil, errors.NewNotFound(
corev1.Resource("secrets"), bootstrapHubKubeConfigSecret)
}
return true, bootStrapSecret, nil
})
// Return err since cluster-name cannot be found in hubkubeconfig secret
err = controller.controller.sync(nil, syncContext)
if err == nil {
t.Errorf("Expected error when sync")
}
nucleusAction = controller.nucleusClient.Actions()
if len(nucleusAction) != 4 {
t.Errorf("Expect 4 actions in the sync loop, actual %#v", nucleusAction)
}
assertAction(t, nucleusAction[3], "update")
assertCondition(t, nucleusAction[3].(clienttesting.UpdateActionImpl).Object, spokeCoreApplied, metav1.ConditionFalse)
// Add hub config secret and sync again
hubSecret.Data["kubeconfig"] = []byte("dummykubeconfig")
hubSecret.Data["cluster-name"] = []byte("cluster1")
controller.kubeClient.PrependReactor("get", "secrets", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
if action.GetVerb() != "get" {
return false, nil, nil
}
getAction := action.(clienttesting.GetActionImpl)
if getAction.Name != hubKubeConfigSecret {
return false, nil, errors.NewNotFound(
corev1.Resource("secrets"), hubKubeConfigSecret)
}
return true, hubSecret, nil
})
err = controller.controller.sync(nil, syncContext)
if err != nil {
t.Errorf("Expected no error when sync: %v", err)
}
nucleusAction = controller.nucleusClient.Actions()
if len(nucleusAction) != 6 {
t.Errorf("Expect 6 actions in the sync loop, actual %#v", nucleusAction)
}
assertAction(t, nucleusAction[5], "update")
assertCondition(t, nucleusAction[5].(clienttesting.UpdateActionImpl).Object, spokeCoreApplied, metav1.ConditionTrue)
}

View File

@@ -50,7 +50,11 @@ spec:
to be deployed on the spoke cluster.
type: array
items:
description: Manifest represents a resource to be deployed on
spoke cluster
type: object
x-kubernetes-preserve-unknown-fields: true
x-kubernetes-embedded-resource: true
status:
description: Status represents the current status of work
type: object

View File

@@ -10,7 +10,6 @@ import (
proto "github.com/gogo/protobuf/proto"
k8s_io_apimachinery_pkg_apis_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
math "math"
math_bits "math/bits"
@@ -29,10 +28,38 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func (m *Manifest) Reset() { *m = Manifest{} }
func (*Manifest) ProtoMessage() {}
func (*Manifest) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{0}
}
func (m *Manifest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Manifest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
func (m *Manifest) XXX_Merge(src proto.Message) {
xxx_messageInfo_Manifest.Merge(m, src)
}
func (m *Manifest) XXX_Size() int {
return m.Size()
}
func (m *Manifest) XXX_DiscardUnknown() {
xxx_messageInfo_Manifest.DiscardUnknown(m)
}
var xxx_messageInfo_Manifest proto.InternalMessageInfo
func (m *ManifestCondition) Reset() { *m = ManifestCondition{} }
func (*ManifestCondition) ProtoMessage() {}
func (*ManifestCondition) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{0}
return fileDescriptor_97234883da270a20, []int{1}
}
func (m *ManifestCondition) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -60,7 +87,7 @@ var xxx_messageInfo_ManifestCondition proto.InternalMessageInfo
func (m *ManifestResourceMeta) Reset() { *m = ManifestResourceMeta{} }
func (*ManifestResourceMeta) ProtoMessage() {}
func (*ManifestResourceMeta) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{1}
return fileDescriptor_97234883da270a20, []int{2}
}
func (m *ManifestResourceMeta) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -88,7 +115,7 @@ var xxx_messageInfo_ManifestResourceMeta proto.InternalMessageInfo
func (m *ManifestResourceStatus) Reset() { *m = ManifestResourceStatus{} }
func (*ManifestResourceStatus) ProtoMessage() {}
func (*ManifestResourceStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{2}
return fileDescriptor_97234883da270a20, []int{3}
}
func (m *ManifestResourceStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -116,7 +143,7 @@ var xxx_messageInfo_ManifestResourceStatus proto.InternalMessageInfo
func (m *ManifestWork) Reset() { *m = ManifestWork{} }
func (*ManifestWork) ProtoMessage() {}
func (*ManifestWork) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{3}
return fileDescriptor_97234883da270a20, []int{4}
}
func (m *ManifestWork) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -144,7 +171,7 @@ var xxx_messageInfo_ManifestWork proto.InternalMessageInfo
func (m *ManifestWorkList) Reset() { *m = ManifestWorkList{} }
func (*ManifestWorkList) ProtoMessage() {}
func (*ManifestWorkList) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{4}
return fileDescriptor_97234883da270a20, []int{5}
}
func (m *ManifestWorkList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -172,7 +199,7 @@ var xxx_messageInfo_ManifestWorkList proto.InternalMessageInfo
func (m *ManifestWorkSpec) Reset() { *m = ManifestWorkSpec{} }
func (*ManifestWorkSpec) ProtoMessage() {}
func (*ManifestWorkSpec) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{5}
return fileDescriptor_97234883da270a20, []int{6}
}
func (m *ManifestWorkSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -200,7 +227,7 @@ var xxx_messageInfo_ManifestWorkSpec proto.InternalMessageInfo
func (m *ManifestWorkStatus) Reset() { *m = ManifestWorkStatus{} }
func (*ManifestWorkStatus) ProtoMessage() {}
func (*ManifestWorkStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{6}
return fileDescriptor_97234883da270a20, []int{7}
}
func (m *ManifestWorkStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -228,7 +255,7 @@ var xxx_messageInfo_ManifestWorkStatus proto.InternalMessageInfo
func (m *ManifestsTemplate) Reset() { *m = ManifestsTemplate{} }
func (*ManifestsTemplate) ProtoMessage() {}
func (*ManifestsTemplate) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{7}
return fileDescriptor_97234883da270a20, []int{8}
}
func (m *ManifestsTemplate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -256,7 +283,7 @@ var xxx_messageInfo_ManifestsTemplate proto.InternalMessageInfo
func (m *StatusCondition) Reset() { *m = StatusCondition{} }
func (*StatusCondition) ProtoMessage() {}
func (*StatusCondition) Descriptor() ([]byte, []int) {
return fileDescriptor_97234883da270a20, []int{8}
return fileDescriptor_97234883da270a20, []int{9}
}
func (m *StatusCondition) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -282,6 +309,7 @@ func (m *StatusCondition) XXX_DiscardUnknown() {
var xxx_messageInfo_StatusCondition proto.InternalMessageInfo
func init() {
proto.RegisterType((*Manifest)(nil), "github.com.open_cluster_management.api.work.v1.Manifest")
proto.RegisterType((*ManifestCondition)(nil), "github.com.open_cluster_management.api.work.v1.ManifestCondition")
proto.RegisterType((*ManifestResourceMeta)(nil), "github.com.open_cluster_management.api.work.v1.ManifestResourceMeta")
proto.RegisterType((*ManifestResourceStatus)(nil), "github.com.open_cluster_management.api.work.v1.ManifestResourceStatus")
@@ -298,63 +326,97 @@ func init() {
}
var fileDescriptor_97234883da270a20 = []byte{
// 884 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcb, 0x8b, 0x1c, 0x45,
0x18, 0x9f, 0xee, 0xec, 0xec, 0xa3, 0x76, 0xdd, 0x24, 0x45, 0x08, 0xcd, 0x1e, 0x7a, 0x97, 0x16,
0x64, 0x15, 0xb7, 0xda, 0x5d, 0x44, 0x3c, 0x88, 0x8f, 0x56, 0x23, 0x62, 0x36, 0x81, 0xca, 0x62,
0x40, 0x24, 0xb1, 0xb6, 0xa7, 0xd2, 0x5b, 0x99, 0xe9, 0xaa, 0xa6, 0xaa, 0x66, 0xe2, 0x5e, 0x44,
0x10, 0xc4, 0x83, 0x07, 0xf1, 0x6f, 0xf1, 0x0f, 0xf0, 0xb8, 0xc7, 0x80, 0x97, 0x9c, 0x06, 0x77,
0xfc, 0x2f, 0x3c, 0x49, 0x55, 0x57, 0x3f, 0x66, 0x66, 0x43, 0xf6, 0x01, 0x9e, 0x66, 0xfa, 0x7b,
0xfc, 0x7e, 0xdf, 0xf7, 0xd5, 0xf7, 0x00, 0x1f, 0x66, 0x4c, 0x1f, 0x0d, 0x0f, 0x51, 0x2a, 0xf2,
0x58, 0x14, 0x94, 0xef, 0xa4, 0x83, 0xa1, 0xd2, 0x54, 0xee, 0xe4, 0x84, 0x93, 0x8c, 0xe6, 0x94,
0xeb, 0x98, 0x14, 0x2c, 0x7e, 0x26, 0x64, 0x3f, 0x1e, 0xed, 0xc6, 0x19, 0xe5, 0x54, 0x12, 0x4d,
0x7b, 0xa8, 0x90, 0x42, 0x0b, 0x88, 0x1a, 0x7f, 0x64, 0xfc, 0x1f, 0x3b, 0xff, 0xc7, 0x8d, 0x3f,
0x22, 0x05, 0x43, 0xc6, 0x1f, 0x8d, 0x76, 0x37, 0x76, 0x5a, 0x7c, 0x99, 0xc8, 0x44, 0x6c, 0x61,
0x0e, 0x87, 0x4f, 0xec, 0x97, 0xfd, 0xb0, 0xff, 0x4a, 0xf8, 0x8d, 0x77, 0xfb, 0xef, 0x2b, 0xc4,
0x84, 0x09, 0x21, 0x27, 0xe9, 0x11, 0xe3, 0x54, 0x1e, 0xc7, 0x45, 0x3f, 0x33, 0x02, 0x15, 0xe7,
0x54, 0x93, 0x33, 0x82, 0xda, 0x88, 0x5f, 0xe6, 0x25, 0x87, 0x5c, 0xb3, 0x9c, 0xce, 0x39, 0xbc,
0xf7, 0x2a, 0x07, 0x95, 0x1e, 0xd1, 0x9c, 0xcc, 0xfa, 0x45, 0xbf, 0xf8, 0xe0, 0xe6, 0x3e, 0xe1,
0xec, 0x09, 0x55, 0xfa, 0x53, 0xc1, 0x7b, 0x4c, 0x33, 0xc1, 0xe1, 0x0f, 0x60, 0x4d, 0x52, 0x25,
0x86, 0x32, 0xa5, 0xfb, 0x54, 0x93, 0xc0, 0xdb, 0xf2, 0xb6, 0x57, 0xf7, 0x3e, 0xbb, 0x60, 0xa9,
0x50, 0x05, 0x8c, 0x5b, 0x58, 0xc9, 0xad, 0x93, 0xf1, 0x66, 0x67, 0x32, 0xde, 0x5c, 0x6b, 0x4b,
0xf1, 0x14, 0x1f, 0x54, 0x00, 0xa4, 0x55, 0x30, 0x2a, 0xf0, 0xb7, 0xae, 0x6d, 0xaf, 0xee, 0x7d,
0x74, 0x51, 0xf6, 0x07, 0x9a, 0xe8, 0xa1, 0xaa, 0x93, 0x4a, 0xa0, 0x23, 0x06, 0xb5, 0x48, 0xe1,
0x16, 0x4d, 0xf4, 0x87, 0x0f, 0x6e, 0x9d, 0x15, 0x31, 0x7c, 0x13, 0x2c, 0x09, 0xd9, 0x63, 0x9c,
0x0c, 0x6c, 0x21, 0xba, 0xc9, 0x75, 0x87, 0xb4, 0x74, 0xbf, 0x14, 0xe3, 0x4a, 0x0f, 0x5f, 0x07,
0xdd, 0x4c, 0x8a, 0x61, 0x11, 0xf8, 0x5b, 0xde, 0xf6, 0x4a, 0xf2, 0x9a, 0x33, 0xec, 0x7e, 0x61,
0x84, 0xb8, 0xd4, 0x19, 0xbc, 0x11, 0x95, 0x8a, 0x09, 0x1e, 0x5c, 0xb3, 0x66, 0x35, 0xde, 0xd7,
0xa5, 0x18, 0x57, 0x7a, 0xb8, 0x05, 0x16, 0xfa, 0x8c, 0xf7, 0x82, 0x05, 0x6b, 0xb7, 0xe6, 0xec,
0x16, 0xbe, 0x62, 0xbc, 0x87, 0xad, 0x06, 0xbe, 0x0d, 0x96, 0xab, 0xd2, 0x05, 0x5d, 0x6b, 0x75,
0xc3, 0x59, 0x2d, 0x57, 0x49, 0xe0, 0xda, 0xc2, 0xe0, 0x71, 0x92, 0xd3, 0x60, 0x71, 0x1a, 0xef,
0x1e, 0xc9, 0x29, 0xb6, 0x1a, 0x18, 0x83, 0x15, 0xf3, 0xab, 0x0a, 0x92, 0xd2, 0x60, 0xc9, 0x9a,
0xdd, 0x74, 0x66, 0x2b, 0xf7, 0x2a, 0x05, 0x6e, 0x6c, 0xa2, 0x5f, 0x3d, 0x70, 0x7b, 0xb6, 0x6c,
0x65, 0xe9, 0xa1, 0x04, 0x2b, 0xb9, 0xd3, 0x54, 0xaf, 0xf8, 0xc9, 0x65, 0x7b, 0xa8, 0x79, 0xc7,
0x3a, 0x9c, 0x4a, 0xa5, 0x70, 0x43, 0x13, 0xfd, 0xe9, 0x83, 0xb5, 0x4a, 0xf1, 0x50, 0xc8, 0x3e,
0xfc, 0x0e, 0x2c, 0x9b, 0x29, 0xeb, 0x91, 0xba, 0x8f, 0xdf, 0x41, 0xe5, 0xb0, 0xa0, 0xf6, 0xb0,
0xa0, 0xa2, 0x9f, 0x19, 0x81, 0x42, 0xc6, 0xda, 0x30, 0xdf, 0x3f, 0x7c, 0x4a, 0x53, 0x6d, 0x7b,
0xb6, 0x6e, 0x9d, 0x46, 0x86, 0x6b, 0x54, 0x78, 0x08, 0x16, 0x54, 0x41, 0x53, 0xfb, 0xe6, 0xab,
0x7b, 0x1f, 0x5f, 0x36, 0x43, 0x13, 0xed, 0x83, 0x82, 0xa6, 0xcd, 0xb3, 0x98, 0x2f, 0x6c, 0xb1,
0xe1, 0x53, 0xb0, 0xa8, 0x6c, 0x51, 0x6d, 0xcb, 0xac, 0xee, 0x25, 0x57, 0x62, 0xb1, 0x48, 0xc9,
0xba, 0xe3, 0x59, 0x2c, 0xbf, 0xb1, 0x63, 0x88, 0xfe, 0xf2, 0xc0, 0x8d, 0xb6, 0xf9, 0x5d, 0xa6,
0x34, 0xfc, 0x76, 0xae, 0x8c, 0xe8, 0x7c, 0x65, 0x34, 0xde, 0xb6, 0x88, 0x75, 0x5f, 0x56, 0x92,
0x56, 0x09, 0x09, 0xe8, 0x32, 0x4d, 0xf3, 0xaa, 0x4b, 0x3e, 0xb8, 0x4a, 0x76, 0xcd, 0xd4, 0x7d,
0x69, 0x20, 0x71, 0x89, 0x1c, 0xfd, 0x34, 0x93, 0x95, 0x29, 0x2e, 0x14, 0x60, 0xd9, 0x40, 0x0c,
0x04, 0xe9, 0xb9, 0xac, 0x2e, 0xdd, 0xa0, 0xea, 0x80, 0xe6, 0xc5, 0x80, 0x68, 0xda, 0x24, 0xfa,
0xd0, 0x41, 0xe3, 0x9a, 0x24, 0xfa, 0xdd, 0x07, 0x70, 0xfe, 0x29, 0x66, 0x16, 0x9e, 0xf7, 0xbf,
0x2c, 0x3c, 0xf8, 0xb3, 0x07, 0xd6, 0xe5, 0xd4, 0xc4, 0xba, 0x16, 0xbe, 0x73, 0xd5, 0x45, 0xef,
0x1a, 0xec, 0xb6, 0x0b, 0x60, 0x7d, 0x5a, 0x8e, 0x67, 0x58, 0x23, 0xd5, 0xdc, 0xa0, 0xba, 0x8a,
0xf0, 0xd1, 0xfc, 0xf2, 0xd8, 0x79, 0x69, 0xc7, 0xb9, 0x2b, 0x87, 0x30, 0x79, 0xf6, 0xf9, 0xf7,
0x9a, 0x72, 0xf5, 0xea, 0x45, 0x31, 0xf6, 0xc1, 0xf5, 0x99, 0x8a, 0x99, 0xf5, 0xa8, 0x8f, 0x0b,
0x6a, 0x5b, 0xa1, 0xb5, 0x1e, 0x0f, 0x8e, 0x0b, 0x8a, 0xad, 0x06, 0x3e, 0xaa, 0xe7, 0xb0, 0xdc,
0xf0, 0x77, 0xa6, 0x67, 0xe8, 0xdf, 0xf1, 0xe6, 0xb9, 0x0e, 0x3e, 0xaa, 0x39, 0xa7, 0x67, 0x0f,
0x8e, 0x00, 0x1c, 0x10, 0xa5, 0x0f, 0x24, 0xe1, 0xca, 0xea, 0x0f, 0x58, 0x4e, 0xdd, 0xcc, 0xbf,
0x75, 0xbe, 0x81, 0x33, 0x1e, 0xc9, 0x86, 0x8b, 0x0b, 0xde, 0x9d, 0x43, 0xc3, 0x67, 0x30, 0xc0,
0x37, 0xc0, 0xa2, 0xa4, 0x44, 0x09, 0xee, 0x4e, 0x4d, 0xbd, 0x1b, 0xb0, 0x95, 0x62, 0xa7, 0x35,
0xb7, 0x2b, 0xa7, 0x4a, 0x91, 0xac, 0xba, 0x36, 0xf5, 0xed, 0xda, 0x2f, 0xc5, 0xb8, 0xd2, 0x27,
0xdb, 0x27, 0xa7, 0x61, 0xe7, 0xf9, 0x69, 0xd8, 0x79, 0x71, 0x1a, 0x76, 0x7e, 0x9c, 0x84, 0xde,
0xc9, 0x24, 0xf4, 0x9e, 0x4f, 0x42, 0xef, 0xc5, 0x24, 0xf4, 0xfe, 0x9e, 0x84, 0xde, 0x6f, 0xff,
0x84, 0x9d, 0x6f, 0xfc, 0xd1, 0xee, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x46, 0xb3, 0xb5, 0x00,
0xc3, 0x09, 0x00, 0x00,
// 911 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4b, 0x6b, 0x24, 0x45,
0x1c, 0x9f, 0x9e, 0xcd, 0x24, 0x93, 0xca, 0x98, 0xdd, 0x2d, 0x96, 0x65, 0xc8, 0xa1, 0x13, 0x5a,
0x90, 0x28, 0xa6, 0xda, 0x04, 0x91, 0x3d, 0x88, 0x8f, 0x56, 0x57, 0xc4, 0xcd, 0x2e, 0xd4, 0x06,
0x17, 0x44, 0x5c, 0x2b, 0x3d, 0xb5, 0x9d, 0xda, 0x99, 0xae, 0x6a, 0xaa, 0x6a, 0x66, 0xcd, 0x41,
0x11, 0x04, 0xf1, 0xe0, 0x41, 0xfc, 0x2c, 0x7e, 0x00, 0x8f, 0x39, 0x2e, 0x78, 0xd9, 0x53, 0x30,
0xe3, 0xb7, 0xf0, 0x24, 0x55, 0x5d, 0x5d, 0xdd, 0x33, 0xc9, 0x62, 0x1e, 0xe0, 0x29, 0xe9, 0xff,
0xe3, 0xf7, 0xfb, 0xbf, 0x6b, 0xc0, 0x7b, 0x19, 0xd3, 0x07, 0xe3, 0x7d, 0x94, 0x8a, 0x3c, 0x16,
0x05, 0xe5, 0x5b, 0xe9, 0x68, 0xac, 0x34, 0x95, 0x5b, 0x39, 0xe1, 0x24, 0xa3, 0x39, 0xe5, 0x3a,
0x26, 0x05, 0x8b, 0x9f, 0x09, 0x39, 0x8c, 0x27, 0xdb, 0x71, 0x46, 0x39, 0x95, 0x44, 0xd3, 0x01,
0x2a, 0xa4, 0xd0, 0x02, 0xa2, 0xda, 0x1f, 0x19, 0xff, 0xc7, 0xce, 0xff, 0x71, 0xed, 0x8f, 0x48,
0xc1, 0x90, 0xf1, 0x47, 0x93, 0xed, 0xb5, 0xad, 0x06, 0x5f, 0x26, 0x32, 0x11, 0x5b, 0x98, 0xfd,
0xf1, 0x13, 0xfb, 0x65, 0x3f, 0xec, 0x7f, 0x25, 0xfc, 0xda, 0xdb, 0xc3, 0x3b, 0x0a, 0x31, 0x61,
0x42, 0xc8, 0x49, 0x7a, 0xc0, 0x38, 0x95, 0x87, 0x71, 0x31, 0xcc, 0x8c, 0x40, 0xc5, 0x39, 0xd5,
0xe4, 0x8c, 0xa0, 0xd6, 0xe2, 0x97, 0x79, 0xc9, 0x31, 0xd7, 0x2c, 0xa7, 0xa7, 0x1c, 0xde, 0xf9,
0x2f, 0x07, 0x95, 0x1e, 0xd0, 0x9c, 0xcc, 0xfb, 0x45, 0x0a, 0x74, 0x77, 0x09, 0x67, 0x4f, 0xa8,
0xd2, 0x30, 0x03, 0x3d, 0x49, 0x9e, 0x7d, 0xf2, 0xad, 0xa6, 0x5c, 0x31, 0xc1, 0xfb, 0xc1, 0x46,
0xb0, 0xb9, 0xb2, 0xb3, 0x85, 0x4a, 0x68, 0xd4, 0x84, 0x46, 0xc5, 0x30, 0x43, 0x0e, 0x1a, 0xe1,
0x86, 0x53, 0x72, 0xeb, 0xe8, 0x78, 0xbd, 0x35, 0x3d, 0x5e, 0xef, 0x35, 0xa5, 0x78, 0x06, 0x38,
0xfa, 0xb9, 0x0d, 0x6e, 0x56, 0xac, 0x1f, 0x09, 0x3e, 0x60, 0x9a, 0x09, 0x0e, 0xbf, 0x07, 0x3d,
0x49, 0x95, 0x18, 0xcb, 0x94, 0xee, 0x52, 0x4d, 0x1c, 0xfd, 0xc7, 0x17, 0xec, 0x0f, 0xaa, 0x80,
0x71, 0x03, 0xab, 0x11, 0x55, 0x43, 0x8a, 0x67, 0xf8, 0xa0, 0x02, 0x20, 0xad, 0x82, 0x51, 0xfd,
0xf6, 0xc6, 0xb5, 0xcd, 0x95, 0x9d, 0xf7, 0x2f, 0xca, 0xfe, 0x50, 0x13, 0x3d, 0x56, 0x3e, 0xa9,
0x04, 0x3a, 0x62, 0xe0, 0x45, 0x0a, 0x37, 0x68, 0xa2, 0xdf, 0xdb, 0xe0, 0xd6, 0x59, 0x11, 0xc3,
0xd7, 0xc1, 0x92, 0x90, 0x03, 0xc6, 0xc9, 0xc8, 0x16, 0xa2, 0x93, 0x5c, 0x77, 0x48, 0x4b, 0x0f,
0x4a, 0x31, 0xae, 0xf4, 0xf0, 0x55, 0xd0, 0xc9, 0xa4, 0x18, 0x17, 0xfd, 0xf6, 0x46, 0xb0, 0xb9,
0x9c, 0xbc, 0xe2, 0x0c, 0x3b, 0x9f, 0x1a, 0x21, 0x2e, 0x75, 0x06, 0x6f, 0x42, 0xa5, 0xed, 0xeb,
0x35, 0x6b, 0xe6, 0xf1, 0xbe, 0x28, 0xc5, 0xb8, 0xd2, 0xc3, 0x0d, 0xb0, 0x30, 0x64, 0x7c, 0xd0,
0x5f, 0xb0, 0x76, 0x3d, 0x67, 0xb7, 0xf0, 0x39, 0xe3, 0x03, 0x6c, 0x35, 0xf0, 0x4d, 0xd0, 0xad,
0x4a, 0xd7, 0xef, 0x58, 0xab, 0x1b, 0xce, 0xaa, 0x5b, 0x25, 0x81, 0xbd, 0x85, 0xc1, 0xe3, 0x24,
0xa7, 0xfd, 0xc5, 0x59, 0xbc, 0xfb, 0x24, 0xa7, 0xd8, 0x6a, 0x60, 0x0c, 0x96, 0xcd, 0x5f, 0x55,
0x90, 0x94, 0xf6, 0x97, 0xac, 0xd9, 0x4d, 0x67, 0xb6, 0x7c, 0xbf, 0x52, 0xe0, 0xda, 0x26, 0xfa,
0x25, 0x00, 0xb7, 0xe7, 0xcb, 0x56, 0x96, 0x1e, 0x4a, 0xb0, 0x9c, 0x3b, 0x4d, 0xd5, 0xc5, 0x0f,
0x2f, 0x3b, 0x43, 0x75, 0x1f, 0x7d, 0x38, 0x95, 0x4a, 0xe1, 0x9a, 0x26, 0xfa, 0xa3, 0x0d, 0x7a,
0x95, 0xe2, 0x91, 0x90, 0x43, 0xf8, 0x0d, 0xe8, 0x9a, 0xd5, 0x1e, 0x10, 0x3f, 0xc7, 0x6f, 0xbd,
0x74, 0x8d, 0xcc, 0x21, 0x40, 0xc6, 0xda, 0x30, 0x3f, 0xd8, 0x7f, 0x4a, 0x53, 0x6d, 0x67, 0xd6,
0x8f, 0x4e, 0x2d, 0xc3, 0x1e, 0x15, 0xee, 0x83, 0x05, 0x55, 0xd0, 0xd4, 0xf6, 0x7c, 0x65, 0xe7,
0x83, 0xcb, 0x66, 0x68, 0xa2, 0x7d, 0x58, 0xd0, 0xb4, 0x6e, 0x8b, 0xf9, 0xc2, 0x16, 0x1b, 0x3e,
0x05, 0x8b, 0xca, 0x16, 0xd5, 0x8e, 0xcc, 0xca, 0x4e, 0x72, 0x25, 0x16, 0x8b, 0x94, 0xac, 0x3a,
0x9e, 0xc5, 0xf2, 0x1b, 0x3b, 0x86, 0xe8, 0xcf, 0x00, 0xdc, 0x68, 0x9a, 0xdf, 0x63, 0x4a, 0xc3,
0xaf, 0x4e, 0x95, 0x11, 0x9d, 0xaf, 0x8c, 0xc6, 0xdb, 0x16, 0xd1, 0xcf, 0x65, 0x25, 0x69, 0x94,
0x90, 0x80, 0x0e, 0xd3, 0x34, 0xaf, 0xa6, 0xe4, 0xdd, 0xab, 0x64, 0x57, 0x6f, 0xdd, 0x67, 0x06,
0x12, 0x97, 0xc8, 0xd1, 0x8f, 0x73, 0x59, 0x99, 0xe2, 0x42, 0x01, 0xba, 0x06, 0x62, 0x24, 0xc8,
0xc0, 0x65, 0x75, 0xe9, 0x01, 0x55, 0x7b, 0x34, 0x2f, 0x46, 0x44, 0xd3, 0x3a, 0xd1, 0x47, 0x0e,
0x1a, 0x7b, 0x92, 0xe8, 0xb7, 0x36, 0x80, 0xa7, 0x5b, 0x31, 0x77, 0xf0, 0x82, 0xff, 0xe5, 0xe0,
0xc1, 0x9f, 0x02, 0xb0, 0x2a, 0x67, 0x36, 0xd6, 0x8d, 0xf0, 0xdd, 0xab, 0x1e, 0x7a, 0x37, 0x60,
0xb7, 0x5d, 0x00, 0xab, 0xb3, 0x72, 0x3c, 0xc7, 0x1a, 0x7d, 0x57, 0xbf, 0x41, 0xbe, 0x8a, 0xf0,
0xa0, 0x79, 0x3c, 0xca, 0x8a, 0xdc, 0xb9, 0x6c, 0x5c, 0x75, 0x4b, 0x7c, 0xa4, 0x8d, 0x93, 0x71,
0xdc, 0x06, 0xd7, 0xe7, 0x6a, 0x67, 0x0e, 0xa5, 0x3e, 0x2c, 0xa8, 0x1d, 0x8a, 0xc6, 0xa1, 0xdc,
0x3b, 0x2c, 0x28, 0xb6, 0x1a, 0xf8, 0xb5, 0xdf, 0xc8, 0xf2, 0xd6, 0xdf, 0x9d, 0xdd, 0xa6, 0x7f,
0x8e, 0xd7, 0xcf, 0xf5, 0x7b, 0x03, 0x79, 0xce, 0xd9, 0x2d, 0x84, 0x13, 0x00, 0x47, 0x44, 0xe9,
0x3d, 0x49, 0xb8, 0xb2, 0xfa, 0x3d, 0x96, 0x53, 0xb7, 0xfd, 0x6f, 0x9c, 0x6f, 0xf5, 0x8c, 0x47,
0xb2, 0xe6, 0xe2, 0x82, 0xf7, 0x4e, 0xa1, 0xe1, 0x33, 0x18, 0xe0, 0x6b, 0x60, 0x51, 0x52, 0xa2,
0x04, 0x77, 0x8f, 0x8e, 0xbf, 0x12, 0xd8, 0x4a, 0xb1, 0xd3, 0x9a, 0x57, 0x2c, 0xa7, 0x4a, 0x91,
0xac, 0x7a, 0x77, 0xfc, 0x2b, 0xb6, 0x5b, 0x8a, 0x71, 0xa5, 0x4f, 0x36, 0x8f, 0x4e, 0xc2, 0xd6,
0xf3, 0x93, 0xb0, 0xf5, 0xe2, 0x24, 0x6c, 0xfd, 0x30, 0x0d, 0x83, 0xa3, 0x69, 0x18, 0x3c, 0x9f,
0x86, 0xc1, 0x8b, 0x69, 0x18, 0xfc, 0x35, 0x0d, 0x83, 0x5f, 0xff, 0x0e, 0x5b, 0x5f, 0xb6, 0x27,
0xdb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x4c, 0x96, 0x74, 0x42, 0x0a, 0x00, 0x00,
}
func (m *Manifest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Manifest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Manifest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
{
size, err := m.RawExtension.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *ManifestCondition) Marshal() (dAtA []byte, err error) {
@@ -697,10 +759,10 @@ func (m *ManifestsTemplate) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.Manifests) > 0 {
for iNdEx := len(m.Manifests) - 1; iNdEx >= 0; iNdEx-- {
if len(m.Manifest) > 0 {
for iNdEx := len(m.Manifest) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Manifests[iNdEx].MarshalToSizedBuffer(dAtA[:i])
size, err := m.Manifest[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
@@ -708,7 +770,7 @@ func (m *ManifestsTemplate) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
@@ -778,6 +840,17 @@ func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
return base
}
func (m *Manifest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.RawExtension.Size()
n += 1 + l + sovGenerated(uint64(l))
return n
}
func (m *ManifestCondition) Size() (n int) {
if m == nil {
return 0
@@ -898,8 +971,8 @@ func (m *ManifestsTemplate) Size() (n int) {
}
var l int
_ = l
if len(m.Manifests) > 0 {
for _, e := range m.Manifests {
if len(m.Manifest) > 0 {
for _, e := range m.Manifest {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
@@ -932,6 +1005,16 @@ func sovGenerated(x uint64) (n int) {
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (this *Manifest) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&Manifest{`,
`RawExtension:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.RawExtension), "RawExtension", "runtime.RawExtension", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
}
func (this *ManifestCondition) String() string {
if this == nil {
return "nil"
@@ -1037,13 +1120,13 @@ func (this *ManifestsTemplate) String() string {
if this == nil {
return "nil"
}
repeatedStringForManifests := "[]RawExtension{"
for _, f := range this.Manifests {
repeatedStringForManifests += fmt.Sprintf("%v", f) + ","
repeatedStringForManifest := "[]Manifest{"
for _, f := range this.Manifest {
repeatedStringForManifest += strings.Replace(strings.Replace(f.String(), "Manifest", "Manifest", 1), `&`, ``, 1) + ","
}
repeatedStringForManifests += "}"
repeatedStringForManifest += "}"
s := strings.Join([]string{`&ManifestsTemplate{`,
`Manifests:` + repeatedStringForManifests + `,`,
`Manifest:` + repeatedStringForManifest + `,`,
`}`,
}, "")
return s
@@ -1070,6 +1153,92 @@ func valueToStringGenerated(v interface{}) string {
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *Manifest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Manifest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Manifest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field RawExtension", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.RawExtension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *ManifestCondition) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -2048,9 +2217,9 @@ func (m *ManifestsTemplate) Unmarshal(dAtA []byte) error {
return fmt.Errorf("proto: ManifestsTemplate: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 2:
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Manifests", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field Manifest", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
@@ -2077,8 +2246,8 @@ func (m *ManifestsTemplate) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Manifests = append(m.Manifests, runtime.RawExtension{})
if err := m.Manifests[len(m.Manifests)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
m.Manifest = append(m.Manifest, Manifest{})
if err := m.Manifest[len(m.Manifest)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex

View File

@@ -12,6 +12,13 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1";
// Manifest represents a resource to be deployed on spoke cluster
message Manifest {
// +kubebuilder:validation:EmbeddedResource
// +kubebuilder:pruning:PreserveUnknownFields
optional k8s.io.apimachinery.pkg.runtime.RawExtension rawExtension = 1;
}
// ManifestCondition represents the conditions of the resources deployed on
// spoke cluster
message ManifestCondition {
@@ -122,7 +129,7 @@ message ManifestWorkStatus {
message ManifestsTemplate {
// Manifests represents a list of kuberenetes resources to be deployed on the spoke cluster.
// +optional
repeated k8s.io.apimachinery.pkg.runtime.RawExtension manifests = 2;
repeated Manifest manifests = 1;
}
// StatusCondition contains condition information for a spoke work.

View File

@@ -55,11 +55,18 @@ type ManifestWorkSpec struct {
Workload ManifestsTemplate `json:"workload,omitempty" protobuf:"bytes,1,opt,name=workload"`
}
// Manifest represents a resource to be deployed on spoke cluster
type Manifest struct {
// +kubebuilder:validation:EmbeddedResource
// +kubebuilder:pruning:PreserveUnknownFields
runtime.RawExtension `json:",inline" protobuf:"bytes,1,opt,name=rawExtension"`
}
// ManifestsTemplate represents the manifest workload to be deployed on spoke cluster
type ManifestsTemplate struct {
// Manifests represents a list of kuberenetes resources to be deployed on the spoke cluster.
// +optional
Manifests []runtime.RawExtension `json:"manifests,omitempty" protobuf:"bytes,2,rep,name=manifests"`
Manifest []Manifest `json:"manifests,omitempty" protobuf:"bytes,1,rep,name=manifests"`
}
// ManifestResourceMeta represents the gvk, gvr, name and namespace of a resoure

View File

@@ -8,6 +8,23 @@ 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 *Manifest) DeepCopyInto(out *Manifest) {
*out = *in
in.RawExtension.DeepCopyInto(&out.RawExtension)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Manifest.
func (in *Manifest) DeepCopy() *Manifest {
if in == nil {
return nil
}
out := new(Manifest)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ManifestCondition) DeepCopyInto(out *ManifestCondition) {
*out = *in
@@ -176,9 +193,9 @@ func (in *ManifestWorkStatus) DeepCopy() *ManifestWorkStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ManifestsTemplate) DeepCopyInto(out *ManifestsTemplate) {
*out = *in
if in.Manifests != nil {
in, out := &in.Manifests, &out.Manifests
*out = make([]runtime.RawExtension, len(*in))
if in.Manifest != nil {
in, out := &in.Manifest, &out.Manifest
*out = make([]Manifest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}

View File

@@ -11,6 +11,14 @@ package v1
// Those methods can be generated by using hack/update-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE
var map_Manifest = map[string]string{
"": "Manifest represents a resource to be deployed on spoke cluster",
}
func (Manifest) SwaggerDoc() map[string]string {
return map_Manifest
}
var map_ManifestCondition = map[string]string{
"": "ManifestCondition represents the conditions of the resources deployed on spoke cluster",
"resourceMeta": "ResourceMeta represents the gvk, name and namespace of a resoure",

2
vendor/modules.txt vendored
View File

@@ -89,7 +89,7 @@ github.com/modern-go/concurrent
github.com/modern-go/reflect2
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
github.com/munnerz/goautoneg
# github.com/open-cluster-management/api v0.0.0-20200506150956-355c9d6ef16b
# github.com/open-cluster-management/api v0.0.0-20200512021938-5f0d5c12ea67
github.com/open-cluster-management/api/client/nucleus/clientset/versioned
github.com/open-cluster-management/api/client/nucleus/clientset/versioned/fake
github.com/open-cluster-management/api/client/nucleus/clientset/versioned/scheme