mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-05-14 05:07:48 +00:00
3
Makefile
3
Makefile
@@ -51,11 +51,12 @@ endif
|
||||
|
||||
$(call add-bindata,cluster-manager,./manifests/cluster-manager/...,bindata,bindata,./pkg/operators/clustermanager/bindata/bindata.go)
|
||||
$(call add-bindata,klusterlet,./manifests/klusterlet/...,bindata,bindata,./pkg/operators/klusterlet/bindata/bindata.go)
|
||||
$(call add-bindata,klusterletkube111,./manifests/klusterletkube111/...,kube111bindata,kube111bindata,./pkg/operators/klusterlet/kube111bindata/bindata.go)
|
||||
|
||||
copy-crd:
|
||||
bash -x hack/copy-crds.sh
|
||||
|
||||
update-all: copy-crd update-bindata-cluster-manager update-bindata-klusterlet update-csv
|
||||
update-all: copy-crd update-bindata-cluster-manager update-bindata-klusterlet update-bindata-klusterletkube111 update-csv
|
||||
|
||||
verify-crds:
|
||||
bash -x hack/verify-crds.sh
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Create the cluster rolebinding so klusterlet can deploy others
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: open-cluster-management:{{ .KlusterletName }}-registration-operator:operator-kube111
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: klusterlet
|
||||
namespace: {{ .OperatorNamespace }}
|
||||
@@ -0,0 +1,13 @@
|
||||
# create this clusterrolebinding so work agent is able to create any clusterrole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: open-cluster-management:{{ .KlusterletName }}-work:agent-kube111
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .KlusterletName }}-work-sa
|
||||
namespace: {{ .KlusterletNamespace }}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
appsinformer "k8s.io/client-go/informers/apps/v1"
|
||||
coreinformer "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
operatorapiv1 "github.com/open-cluster-management/api/operator/v1"
|
||||
"github.com/open-cluster-management/registration-operator/pkg/helpers"
|
||||
"github.com/open-cluster-management/registration-operator/pkg/operators/klusterlet/bindata"
|
||||
"github.com/open-cluster-management/registration-operator/pkg/operators/klusterlet/kube111bindata"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,12 +50,19 @@ var (
|
||||
"manifests/klusterlet/klusterlet-work-clusterrolebinding.yaml",
|
||||
"manifests/klusterlet/klusterlet-work-clusterrolebinding-addition.yaml",
|
||||
}
|
||||
|
||||
kube111StaticResourceFiles = []string{
|
||||
"manifests/klusterletkube111/klusterlet-registration-operator-clusterrolebinding.yaml",
|
||||
"manifests/klusterletkube111/klusterlet-work-clusterrolebinding.yaml",
|
||||
}
|
||||
)
|
||||
|
||||
type klusterletController struct {
|
||||
klusterletClient operatorv1client.KlusterletInterface
|
||||
klusterletLister operatorlister.KlusterletLister
|
||||
kubeClient kubernetes.Interface
|
||||
klusterletClient operatorv1client.KlusterletInterface
|
||||
klusterletLister operatorlister.KlusterletLister
|
||||
kubeClient kubernetes.Interface
|
||||
kubeVersion *version.Version
|
||||
operatorNamespace string
|
||||
}
|
||||
|
||||
// NewKlusterletController construct klusterlet controller
|
||||
@@ -63,11 +72,15 @@ func NewKlusterletController(
|
||||
klusterletInformer operatorinformer.KlusterletInformer,
|
||||
secretInformer coreinformer.SecretInformer,
|
||||
deploymentInformer appsinformer.DeploymentInformer,
|
||||
kubeVersion *version.Version,
|
||||
operatorNamespace string,
|
||||
recorder events.Recorder) factory.Controller {
|
||||
controller := &klusterletController{
|
||||
kubeClient: kubeClient,
|
||||
klusterletClient: klusterletClient,
|
||||
klusterletLister: klusterletInformer.Lister(),
|
||||
kubeClient: kubeClient,
|
||||
klusterletClient: klusterletClient,
|
||||
klusterletLister: klusterletInformer.Lister(),
|
||||
kubeVersion: kubeVersion,
|
||||
operatorNamespace: operatorNamespace,
|
||||
}
|
||||
|
||||
return factory.New().WithSync(controller.sync).
|
||||
@@ -90,6 +103,7 @@ type klusterletConfig struct {
|
||||
ExternalServerURL string
|
||||
HubKubeConfigSecret string
|
||||
BootStrapKubeConfigSecret string
|
||||
OperatorNamespace string
|
||||
}
|
||||
|
||||
func (n *klusterletController) sync(ctx context.Context, controllerContext factory.SyncContext) error {
|
||||
@@ -114,6 +128,7 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
BootStrapKubeConfigSecret: helpers.BootstrapHubKubeConfigSecret,
|
||||
HubKubeConfigSecret: helpers.HubKubeConfigSecret,
|
||||
ExternalServerURL: getServersFromKlusterlet(klusterlet),
|
||||
OperatorNamespace: n.operatorNamespace,
|
||||
}
|
||||
// If namespace is not set, use the default namespace
|
||||
if config.KlusterletNamespace == "" {
|
||||
@@ -167,7 +182,25 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
return err
|
||||
}
|
||||
|
||||
// Deploy the static resources
|
||||
errs := []error{}
|
||||
// If kube version is less than 1.12, deploy static resource for kube 1.11 at first
|
||||
// TODO remove this when we do not support kube 1.11 any longer
|
||||
if cnt, err := n.kubeVersion.Compare("v1.12.0"); err == nil && cnt < 0 {
|
||||
resourceResult := resourceapply.ApplyDirectly(
|
||||
resourceapply.NewKubeClientHolder(n.kubeClient),
|
||||
controllerContext.Recorder(),
|
||||
func(name string) ([]byte, error) {
|
||||
return assets.MustCreateAssetFromTemplate(name, kube111bindata.MustAsset(filepath.Join("", name)), config).Data, nil
|
||||
},
|
||||
kube111StaticResourceFiles...,
|
||||
)
|
||||
for _, result := range resourceResult {
|
||||
if result.Error != nil {
|
||||
errs = append(errs, fmt.Errorf("%q (%T): %v", result.File, result.Type, result.Error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply static files
|
||||
resourceResults := resourceapply.ApplyDirectly(
|
||||
resourceapply.NewKubeClientHolder(n.kubeClient),
|
||||
@@ -177,7 +210,7 @@ func (n *klusterletController) sync(ctx context.Context, controllerContext facto
|
||||
},
|
||||
staticResourceFiles...,
|
||||
)
|
||||
errs := []error{}
|
||||
|
||||
for _, result := range resourceResults {
|
||||
if result.Error != nil {
|
||||
errs = append(errs, fmt.Errorf("%q (%T): %v", result.File, result.Type, result.Error))
|
||||
@@ -315,6 +348,27 @@ func (n *klusterletController) cleanUp(ctx context.Context, controllerContext fa
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove this when we do not support kube 1.11 any longer
|
||||
cnt, err := n.kubeVersion.Compare("v1.12.0")
|
||||
klog.Errorf("comapare version %d, %v", cnt, err)
|
||||
if cnt, err := n.kubeVersion.Compare("v1.12.0"); err == nil && cnt < 0 {
|
||||
for _, file := range kube111StaticResourceFiles {
|
||||
err := helpers.CleanUpStaticObject(
|
||||
ctx,
|
||||
n.kubeClient,
|
||||
nil,
|
||||
nil,
|
||||
func(name string) ([]byte, error) {
|
||||
return assets.MustCreateAssetFromTemplate(name, kube111bindata.MustAsset(filepath.Join("", name)), config).Data, nil
|
||||
},
|
||||
file,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
fakekube "k8s.io/client-go/kubernetes/fake"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
@@ -87,11 +88,14 @@ func newTestController(klusterlet *opratorapiv1.Klusterlet, objects ...runtime.O
|
||||
fakeKubeClient := fakekube.NewSimpleClientset(objects...)
|
||||
fakeOperatorClient := fakeoperatorclient.NewSimpleClientset(klusterlet)
|
||||
operatorInformers := operatorinformers.NewSharedInformerFactory(fakeOperatorClient, 5*time.Minute)
|
||||
kubeVersion, _ := version.ParseGeneric("v1.18.0")
|
||||
|
||||
hubController := &klusterletController{
|
||||
klusterletClient: fakeOperatorClient.OperatorV1().Klusterlets(),
|
||||
kubeClient: fakeKubeClient,
|
||||
klusterletLister: operatorInformers.Operator().V1().Klusterlets().Lister(),
|
||||
klusterletClient: fakeOperatorClient.OperatorV1().Klusterlets(),
|
||||
kubeClient: fakeKubeClient,
|
||||
klusterletLister: operatorInformers.Operator().V1().Klusterlets().Lister(),
|
||||
kubeVersion: kubeVersion,
|
||||
operatorNamespace: "open-cluster-management",
|
||||
}
|
||||
|
||||
store := operatorInformers.Operator().V1().Klusterlets().Informer().GetStore()
|
||||
@@ -290,7 +294,7 @@ func TestSyncDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
if len(kubeActions) != 12 {
|
||||
t.Errorf("Expected 7 delete actions, but got %d", len(kubeActions))
|
||||
t.Errorf("Expected 12 delete actions, but got %d", len(kubeActions))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,3 +422,71 @@ func TestClusterNameChange(t *testing.T) {
|
||||
}
|
||||
ensureDeployments(t, controller.kubeClient.Actions(), "update", "https://localhost", "cluster3", "cluster3", 2)
|
||||
}
|
||||
|
||||
func TestDeployOnKube111(t *testing.T) {
|
||||
klusterlet := newKlusterlet("klusterlet", "testns", "cluster1")
|
||||
bootStrapSecret := newSecret(helpers.BootstrapHubKubeConfigSecret, "testns")
|
||||
hubKubeConfigSecret := newSecret(helpers.HubKubeConfigSecret, "testns")
|
||||
hubKubeConfigSecret.Data["kubeconfig"] = []byte("dummuykubeconnfig")
|
||||
namespace := newNamespace("testns")
|
||||
controller := newTestController(klusterlet, bootStrapSecret, hubKubeConfigSecret, namespace)
|
||||
kubeVersion, _ := version.ParseGeneric("v1.11.0")
|
||||
controller.controller.kubeVersion = kubeVersion
|
||||
syncContext := testinghelper.NewFakeSyncContext(t, "klusterlet")
|
||||
|
||||
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) != 13 {
|
||||
t.Errorf("Expect 13 objects created in the sync loop, actual %d", len(createObjects))
|
||||
}
|
||||
for _, object := range createObjects {
|
||||
ensureObject(t, object, klusterlet)
|
||||
}
|
||||
|
||||
operatorAction := controller.operatorClient.Actions()
|
||||
if len(operatorAction) != 2 {
|
||||
t.Errorf("Expect 2 actions in the sync loop, actual %#v", operatorAction)
|
||||
}
|
||||
|
||||
testinghelper.AssertGet(t, operatorAction[0], "operator.open-cluster-management.io", "v1", "klusterlets")
|
||||
testinghelper.AssertAction(t, operatorAction[1], "update")
|
||||
testinghelper.AssertOnlyConditions(
|
||||
t, operatorAction[1].(clienttesting.UpdateActionImpl).Object,
|
||||
testinghelper.NamedCondition(klusterletApplied, "KlusterletApplied", metav1.ConditionTrue))
|
||||
|
||||
// Delete the klusterlet
|
||||
now := metav1.Now()
|
||||
klusterlet.ObjectMeta.SetDeletionTimestamp(&now)
|
||||
controller.operatorStore.Update(klusterlet)
|
||||
controller.kubeClient.ClearActions()
|
||||
err = controller.controller.sync(nil, syncContext)
|
||||
if err != nil {
|
||||
t.Errorf("Expected non error when sync, %v", err)
|
||||
}
|
||||
|
||||
deleteActions := []clienttesting.DeleteActionImpl{}
|
||||
kubeActions = controller.kubeClient.Actions()
|
||||
for _, action := range kubeActions {
|
||||
if action.GetVerb() == "delete" {
|
||||
deleteAction := action.(clienttesting.DeleteActionImpl)
|
||||
deleteActions = append(deleteActions, deleteAction)
|
||||
}
|
||||
}
|
||||
|
||||
if len(kubeActions) != 14 {
|
||||
t.Errorf("Expected 14 delete actions, but got %d", len(kubeActions))
|
||||
}
|
||||
}
|
||||
|
||||
268
pkg/operators/klusterlet/kube111bindata/bindata.go
Normal file
268
pkg/operators/klusterlet/kube111bindata/bindata.go
Normal file
@@ -0,0 +1,268 @@
|
||||
// Code generated for package kube111bindata by go-bindata DO NOT EDIT. (@generated)
|
||||
// sources:
|
||||
// manifests/klusterletkube111/klusterlet-registration-operator-clusterrolebinding.yaml
|
||||
// manifests/klusterletkube111/klusterlet-work-clusterrolebinding.yaml
|
||||
package kube111bindata
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Name return file name
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
|
||||
// Size return file size
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
|
||||
// Mode return file mode
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
|
||||
// Mode return file modify time
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
|
||||
// IsDir return file whether a directory
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return fi.mode&os.ModeDir != 0
|
||||
}
|
||||
|
||||
// Sys return file is sys mode
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYaml = []byte(`# Create the cluster rolebinding so klusterlet can deploy others
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: open-cluster-management:{{ .KlusterletName }}-registration-operator:operator-kube111
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: klusterlet
|
||||
namespace: {{ .OperatorNamespace }}
|
||||
`)
|
||||
|
||||
func manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYamlBytes() ([]byte, error) {
|
||||
return _manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYaml, nil
|
||||
}
|
||||
|
||||
func manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYaml() (*asset, error) {
|
||||
bytes, err := manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYamlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "manifests/klusterletkube111/klusterlet-registration-operator-clusterrolebinding.yaml", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _manifestsKlusterletkube111KlusterletWorkClusterrolebindingYaml = []byte(`# create this clusterrolebinding so work agent is able to create any clusterrole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: open-cluster-management:{{ .KlusterletName }}-work:agent-kube111
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .KlusterletName }}-work-sa
|
||||
namespace: {{ .KlusterletNamespace }}
|
||||
`)
|
||||
|
||||
func manifestsKlusterletkube111KlusterletWorkClusterrolebindingYamlBytes() ([]byte, error) {
|
||||
return _manifestsKlusterletkube111KlusterletWorkClusterrolebindingYaml, nil
|
||||
}
|
||||
|
||||
func manifestsKlusterletkube111KlusterletWorkClusterrolebindingYaml() (*asset, error) {
|
||||
bytes, err := manifestsKlusterletkube111KlusterletWorkClusterrolebindingYamlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "manifests/klusterletkube111/klusterlet-work-clusterrolebinding.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/klusterletkube111/klusterlet-registration-operator-clusterrolebinding.yaml": manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYaml,
|
||||
"manifests/klusterletkube111/klusterlet-work-clusterrolebinding.yaml": manifestsKlusterletkube111KlusterletWorkClusterrolebindingYaml,
|
||||
}
|
||||
|
||||
// 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{
|
||||
"klusterletkube111": {nil, map[string]*bintree{
|
||||
"klusterlet-registration-operator-clusterrolebinding.yaml": {manifestsKlusterletkube111KlusterletRegistrationOperatorClusterrolebindingYaml, map[string]*bintree{}},
|
||||
"klusterlet-work-clusterrolebinding.yaml": {manifestsKlusterletkube111KlusterletWorkClusterrolebindingYaml, 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, "/")...)...)
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package operators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
versionutil "k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
apiregistrationclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||
@@ -20,6 +22,9 @@ import (
|
||||
"github.com/open-cluster-management/registration-operator/pkg/operators/klusterlet/controllers/statuscontroller"
|
||||
)
|
||||
|
||||
// defaultSpokeComponentNamespace is the default namespace in which the operator is deployed
|
||||
const defaultComponentNamespace = "open-cluster-management"
|
||||
|
||||
// RunClusterManagerOperator starts a new cluster manager operator
|
||||
func RunClusterManagerOperator(ctx context.Context, controllerContext *controllercmd.ControllerContext) error {
|
||||
// Build kubclient client and informer for managed cluster
|
||||
@@ -75,6 +80,14 @@ func RunKlusterletOperator(ctx context.Context, controllerContext *controllercmd
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
version, err := kubeClient.ServerVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kubeVersion, err := versionutil.ParseGeneric(version.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kubeInformer := informers.NewSharedInformerFactory(kubeClient, 5*time.Minute)
|
||||
|
||||
@@ -85,12 +98,21 @@ func RunKlusterletOperator(ctx context.Context, controllerContext *controllercmd
|
||||
}
|
||||
operatorInformer := operatorinformer.NewSharedInformerFactory(operatorClient, 5*time.Minute)
|
||||
|
||||
// Read component namespace
|
||||
operatorNamespace := defaultComponentNamespace
|
||||
nsBytes, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
|
||||
if err == nil {
|
||||
operatorNamespace = string(nsBytes)
|
||||
}
|
||||
|
||||
klusterletController := klusterletcontroller.NewKlusterletController(
|
||||
kubeClient,
|
||||
operatorClient.OperatorV1().Klusterlets(),
|
||||
operatorInformer.Operator().V1().Klusterlets(),
|
||||
kubeInformer.Core().V1().Secrets(),
|
||||
kubeInformer.Apps().V1().Deployments(),
|
||||
kubeVersion,
|
||||
operatorNamespace,
|
||||
controllerContext.EventRecorder)
|
||||
statusController := statuscontroller.NewKlusterletStatusController(
|
||||
kubeClient,
|
||||
|
||||
18
vendor/k8s.io/apimachinery/pkg/util/version/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/apimachinery/pkg/util/version/doc.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
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 version provides utilities for version number comparisons
|
||||
package version // import "k8s.io/apimachinery/pkg/util/version"
|
||||
322
vendor/k8s.io/apimachinery/pkg/util/version/version.go
generated
vendored
Normal file
322
vendor/k8s.io/apimachinery/pkg/util/version/version.go
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
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 version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is an opqaue representation of a version number
|
||||
type Version struct {
|
||||
components []uint
|
||||
semver bool
|
||||
preRelease string
|
||||
buildMetadata string
|
||||
}
|
||||
|
||||
var (
|
||||
// versionMatchRE splits a version string into numeric and "extra" parts
|
||||
versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
|
||||
// extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
|
||||
extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
|
||||
)
|
||||
|
||||
func parse(str string, semver bool) (*Version, error) {
|
||||
parts := versionMatchRE.FindStringSubmatch(str)
|
||||
if parts == nil {
|
||||
return nil, fmt.Errorf("could not parse %q as version", str)
|
||||
}
|
||||
numbers, extra := parts[1], parts[2]
|
||||
|
||||
components := strings.Split(numbers, ".")
|
||||
if (semver && len(components) != 3) || (!semver && len(components) < 2) {
|
||||
return nil, fmt.Errorf("illegal version string %q", str)
|
||||
}
|
||||
|
||||
v := &Version{
|
||||
components: make([]uint, len(components)),
|
||||
semver: semver,
|
||||
}
|
||||
for i, comp := range components {
|
||||
if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
num, err := strconv.ParseUint(comp, 10, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
|
||||
}
|
||||
v.components[i] = uint(num)
|
||||
}
|
||||
|
||||
if semver && extra != "" {
|
||||
extraParts := extraMatchRE.FindStringSubmatch(extra)
|
||||
if extraParts == nil {
|
||||
return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
|
||||
}
|
||||
v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
|
||||
|
||||
for _, comp := range strings.Split(v.preRelease, ".") {
|
||||
if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
|
||||
if strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ParseGeneric parses a "generic" version string. The version string must consist of two
|
||||
// or more dot-separated numeric fields (the first of which can't have leading zeroes),
|
||||
// followed by arbitrary uninterpreted data (which need not be separated from the final
|
||||
// numeric field by punctuation). For convenience, leading and trailing whitespace is
|
||||
// ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
|
||||
func ParseGeneric(str string) (*Version, error) {
|
||||
return parse(str, false)
|
||||
}
|
||||
|
||||
// MustParseGeneric is like ParseGeneric except that it panics on error
|
||||
func MustParseGeneric(str string) *Version {
|
||||
v, err := ParseGeneric(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
|
||||
// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
|
||||
// leading and trailing whitespace, and allows the version to be preceded by "v"). For
|
||||
// version strings that are not guaranteed to obey the Semantic Versioning syntax, use
|
||||
// ParseGeneric.
|
||||
func ParseSemantic(str string) (*Version, error) {
|
||||
return parse(str, true)
|
||||
}
|
||||
|
||||
// MustParseSemantic is like ParseSemantic except that it panics on error
|
||||
func MustParseSemantic(str string) *Version {
|
||||
v, err := ParseSemantic(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Major returns the major release number
|
||||
func (v *Version) Major() uint {
|
||||
return v.components[0]
|
||||
}
|
||||
|
||||
// Minor returns the minor release number
|
||||
func (v *Version) Minor() uint {
|
||||
return v.components[1]
|
||||
}
|
||||
|
||||
// Patch returns the patch release number if v is a Semantic Version, or 0
|
||||
func (v *Version) Patch() uint {
|
||||
if len(v.components) < 3 {
|
||||
return 0
|
||||
}
|
||||
return v.components[2]
|
||||
}
|
||||
|
||||
// BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) BuildMetadata() string {
|
||||
return v.buildMetadata
|
||||
}
|
||||
|
||||
// PreRelease returns the prerelease metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) PreRelease() string {
|
||||
return v.preRelease
|
||||
}
|
||||
|
||||
// Components returns the version number components
|
||||
func (v *Version) Components() []uint {
|
||||
return v.components
|
||||
}
|
||||
|
||||
// WithMajor returns copy of the version object with requested major number
|
||||
func (v *Version) WithMajor(major uint) *Version {
|
||||
result := *v
|
||||
result.components = []uint{major, v.Minor(), v.Patch()}
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithMinor returns copy of the version object with requested minor number
|
||||
func (v *Version) WithMinor(minor uint) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), minor, v.Patch()}
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithPatch returns copy of the version object with requested patch number
|
||||
func (v *Version) WithPatch(patch uint) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), v.Minor(), patch}
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithPreRelease returns copy of the version object with requested prerelease
|
||||
func (v *Version) WithPreRelease(preRelease string) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), v.Minor(), v.Patch()}
|
||||
result.preRelease = preRelease
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithBuildMetadata returns copy of the version object with requested buildMetadata
|
||||
func (v *Version) WithBuildMetadata(buildMetadata string) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), v.Minor(), v.Patch()}
|
||||
result.buildMetadata = buildMetadata
|
||||
return &result
|
||||
}
|
||||
|
||||
// String converts a Version back to a string; note that for versions parsed with
|
||||
// ParseGeneric, this will not include the trailing uninterpreted portion of the version
|
||||
// number.
|
||||
func (v *Version) String() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for i, comp := range v.components {
|
||||
if i > 0 {
|
||||
buffer.WriteString(".")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%d", comp))
|
||||
}
|
||||
if v.preRelease != "" {
|
||||
buffer.WriteString("-")
|
||||
buffer.WriteString(v.preRelease)
|
||||
}
|
||||
if v.buildMetadata != "" {
|
||||
buffer.WriteString("+")
|
||||
buffer.WriteString(v.buildMetadata)
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
|
||||
// if they are equal
|
||||
func (v *Version) compareInternal(other *Version) int {
|
||||
|
||||
vLen := len(v.components)
|
||||
oLen := len(other.components)
|
||||
for i := 0; i < vLen && i < oLen; i++ {
|
||||
switch {
|
||||
case other.components[i] < v.components[i]:
|
||||
return 1
|
||||
case other.components[i] > v.components[i]:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// If components are common but one has more items and they are not zeros, it is bigger
|
||||
switch {
|
||||
case oLen < vLen && !onlyZeros(v.components[oLen:]):
|
||||
return 1
|
||||
case oLen > vLen && !onlyZeros(other.components[vLen:]):
|
||||
return -1
|
||||
}
|
||||
|
||||
if !v.semver || !other.semver {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.preRelease == "" && other.preRelease != "":
|
||||
return 1
|
||||
case v.preRelease != "" && other.preRelease == "":
|
||||
return -1
|
||||
case v.preRelease == other.preRelease: // includes case where both are ""
|
||||
return 0
|
||||
}
|
||||
|
||||
vPR := strings.Split(v.preRelease, ".")
|
||||
oPR := strings.Split(other.preRelease, ".")
|
||||
for i := 0; i < len(vPR) && i < len(oPR); i++ {
|
||||
vNum, err := strconv.ParseUint(vPR[i], 10, 0)
|
||||
if err == nil {
|
||||
oNum, err := strconv.ParseUint(oPR[i], 10, 0)
|
||||
if err == nil {
|
||||
switch {
|
||||
case oNum < vNum:
|
||||
return 1
|
||||
case oNum > vNum:
|
||||
return -1
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if oPR[i] < vPR[i] {
|
||||
return 1
|
||||
} else if oPR[i] > vPR[i] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(oPR) < len(vPR):
|
||||
return 1
|
||||
case len(oPR) > len(vPR):
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// returns false if array contain any non-zero element
|
||||
func onlyZeros(array []uint) bool {
|
||||
for _, num := range array {
|
||||
if num != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AtLeast tests if a version is at least equal to a given minimum version. If both
|
||||
// Versions are Semantic Versions, this will use the Semantic Version comparison
|
||||
// algorithm. Otherwise, it will compare only the numeric components, with non-present
|
||||
// components being considered "0" (ie, "1.4" is equal to "1.4.0").
|
||||
func (v *Version) AtLeast(min *Version) bool {
|
||||
return v.compareInternal(min) != -1
|
||||
}
|
||||
|
||||
// LessThan tests if a version is less than a given version. (It is exactly the opposite
|
||||
// of AtLeast, for situations where asking "is v too old?" makes more sense than asking
|
||||
// "is v new enough?".)
|
||||
func (v *Version) LessThan(other *Version) bool {
|
||||
return v.compareInternal(other) == -1
|
||||
}
|
||||
|
||||
// Compare compares v against a version string (which will be parsed as either Semantic
|
||||
// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
|
||||
// it is greater than other, or 0 if they are equal.
|
||||
func (v *Version) Compare(other string) (int, error) {
|
||||
ov, err := parse(other, v.semver)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v.compareInternal(ov), nil
|
||||
}
|
||||
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@@ -526,6 +526,7 @@ k8s.io/apimachinery/pkg/util/strategicpatch
|
||||
k8s.io/apimachinery/pkg/util/uuid
|
||||
k8s.io/apimachinery/pkg/util/validation
|
||||
k8s.io/apimachinery/pkg/util/validation/field
|
||||
k8s.io/apimachinery/pkg/util/version
|
||||
k8s.io/apimachinery/pkg/util/wait
|
||||
k8s.io/apimachinery/pkg/util/waitgroup
|
||||
k8s.io/apimachinery/pkg/util/yaml
|
||||
|
||||
Reference in New Issue
Block a user