diff --git a/pkg/node-servant/convert/convert.go b/pkg/node-servant/convert/convert.go index a2b9ebf2e6c..3b7071369da 100644 --- a/pkg/node-servant/convert/convert.go +++ b/pkg/node-servant/convert/convert.go @@ -17,9 +17,16 @@ limitations under the License. package convert import ( + "context" "fmt" "strings" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/util/retry" + nodeservant "github.com/openyurtio/openyurt/pkg/node-servant" "github.com/openyurtio/openyurt/pkg/node-servant/components" "github.com/openyurtio/openyurt/pkg/yurtadm/constants" @@ -28,6 +35,8 @@ import ( const bootstrapModeKubeletCertificate = "kubeletcertificate" +const multiplexerClusterRoleBindingName = "yurt-hub-multiplexer-binding" + var ( getAPIServerAddressFunc = components.GetAPIServerAddress installYurthubFunc = func(cfg *yurthubutil.YurthubHostConfig) error { @@ -39,6 +48,13 @@ var ( restartContainersFunc = func(nodeName string) error { return components.RestartNonPauseContainers(nodeName, conversionJobPodPrefix(nodeName)) } + getKubeClientFunc = func() (kubernetes.Interface, error) { + cfg, err := rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("failed to get in-cluster config: %w", err) + } + return kubernetes.NewForConfig(cfg) + } ) // Config has the information that required by convert operation. @@ -69,6 +85,9 @@ func NewConverterWithOptions(o *Options) *nodeConverter { // Do is used for the convert job. // shall be implemented as idempotent, can execute multiple times with no side effect. func (n *nodeConverter) Do() error { + if err := n.ensureMultiplexerRBAC(); err != nil { + return fmt.Errorf("failed to configure RBAC: %w", err) + } if err := n.installYurtHub(); err != nil { return err } @@ -94,6 +113,43 @@ func (n *nodeConverter) installYurtHub() error { return installYurthubFunc(n.yurthubHostConfig(apiServerAddress)) } +func (n *nodeConverter) ensureMultiplexerRBAC() error { + client, err := getKubeClientFunc() + if err != nil { + return fmt.Errorf("failed to get Kubernetes client for RBAC setup: %w", err) + } + + return retry.RetryOnConflict(retry.DefaultBackoff, func() error { + crb, err := client.RbacV1().ClusterRoleBindings().Get( + context.TODO(), + multiplexerClusterRoleBindingName, + metav1.GetOptions{}, + ) + if err != nil { + return fmt.Errorf("failed to get ClusterRoleBinding %q: %w", multiplexerClusterRoleBindingName, err) + } + + for _, sub := range crb.Subjects { + if sub.Kind == rbacv1.GroupKind && sub.Name == "system:nodes" { + return nil + } + } + + crb.Subjects = append(crb.Subjects, rbacv1.Subject{ + Kind: rbacv1.GroupKind, + Name: "system:nodes", + APIGroup: rbacv1.GroupName, + }) + + _, err = client.RbacV1().ClusterRoleBindings().Update( + context.TODO(), + crb, + metav1.UpdateOptions{}, + ) + return err + }) +} + func (n *nodeConverter) convertKubelet() error { return redirectKubeletFunc(n.openyurtDir) } diff --git a/pkg/node-servant/convert/convert_test.go b/pkg/node-servant/convert/convert_test.go index 7fe8d565579..5f2ecf4b486 100644 --- a/pkg/node-servant/convert/convert_test.go +++ b/pkg/node-servant/convert/convert_test.go @@ -21,10 +21,26 @@ import ( "reflect" "testing" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" + yurthubutil "github.com/openyurtio/openyurt/pkg/yurtadm/util/yurthub" ) func TestNodeConverterDo(t *testing.T) { + fakeCRB := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: multiplexerClusterRoleBindingName, + }, + Subjects: []rbacv1.Subject{}, + } + + getKubeClientFunc = func() (kubernetes.Interface, error) { + return fake.NewSimpleClientset(fakeCRB), nil + } + oldGetAPIServerAddressFunc := getAPIServerAddressFunc oldInstallYurthubFunc := installYurthubFunc oldRedirectKubeletFunc := redirectKubeletFunc @@ -94,6 +110,7 @@ func TestNodeConverterDo(t *testing.T) { if !reflect.DeepEqual(gotCfg, wantCfg) { t.Fatalf("unexpected yurthub host config, got=%#v, want=%#v", gotCfg, wantCfg) } + } func TestNodeConverterStopsWhenInstallFails(t *testing.T) {