diff --git a/cmd/controller/run.go b/cmd/controller/run.go index 0f46334..54b6eb3 100644 --- a/cmd/controller/run.go +++ b/cmd/controller/run.go @@ -189,7 +189,7 @@ func runController( } if isGKE { - csrMgr := csr.NewApprovalManager(log, clientset) + csrMgr := csr.NewApprovalManager(log, clientset, cfg.ServiceAccount) if err := csrMgr.Start(ctx); err != nil { log.WithError(err).Fatal("failed to start approval manager") } diff --git a/internal/actions/csr/csr.go b/internal/actions/csr/csr.go index b863a08..76d4785 100644 --- a/internal/actions/csr/csr.go +++ b/internal/actions/csr/csr.go @@ -6,6 +6,7 @@ import ( "encoding/pem" "errors" "fmt" + "log" "sort" "strings" "time" @@ -91,11 +92,11 @@ func (c *Certificate) ForCASTAINode() bool { return false } -func (c *Certificate) NodeBootstrap() bool { +func (c *Certificate) NodeBootstrap(serviceAccount string) bool { // Since we only have one handler per CSR/certificate name, // which is the node name, we can process the controller's certificates and kubelet-bootstrap`s. // This covers the case when the controller restarts but the bootstrap certificate was deleted without our own certificate being approved. - return c.RequestingUser == "kubelet-bootstrap" || c.RequestingUser == "system:serviceaccount:castai-agent:castai-cluster-controller" + return c.RequestingUser == "kubelet-bootstrap" || c.RequestingUser == serviceAccount } func isAlreadyApproved(err error) bool { @@ -345,7 +346,7 @@ func createInformer(ctx context.Context, client kubernetes.Interface) (informers var errUnexpectedObjectType = errors.New("unexpected object type") -func processCSREvent(ctx context.Context, c chan<- *Certificate, csrObj interface{}) error { +func processCSREvent(ctx context.Context, c chan<- *Certificate, csrObj interface{}, serviceAccount string) error { cert, err := toCertificate(csrObj) if err != nil { return err @@ -355,7 +356,7 @@ func processCSREvent(ctx context.Context, c chan<- *Certificate, csrObj interfac return nil } - if cert.Approved() || !cert.ForCASTAINode() || !cert.NodeBootstrap() || cert.Outdated() { + if cert.Approved() || !cert.ForCASTAINode() || !cert.NodeBootstrap(serviceAccount) || cert.Outdated() { return nil } @@ -369,10 +370,12 @@ func toCertificate(obj interface{}) (cert *Certificate, err error) { switch e := obj.(type) { case *certv1.CertificateSigningRequest: + log.Printf("certv1.CertificateSigningRequest: %s", e.Name) name = e.Name request = e.Spec.Request cert = &Certificate{Name: name, v1: e, RequestingUser: e.Spec.Username} case *certv1beta1.CertificateSigningRequest: + log.Printf("certv1.CertificateSigningRequest: %s", e.Name) name = e.Name request = e.Spec.Request cert = &Certificate{Name: name, v1Beta1: e, RequestingUser: e.Spec.Username} diff --git a/internal/actions/csr/csr_test.go b/internal/actions/csr/csr_test.go index b60a571..444d2a0 100644 --- a/internal/actions/csr/csr_test.go +++ b/internal/actions/csr/csr_test.go @@ -168,7 +168,7 @@ func Test_nodeBootstrap(t *testing.T) { cert := &Certificate{ RequestingUser: tc.reqUser, } - require.Equal(t, tc.want, cert.NodeBootstrap()) + require.Equal(t, tc.want, cert.NodeBootstrap("system:serviceaccount:castai-agent:castai-cluster-controller")) }) } } @@ -220,7 +220,7 @@ func Test_toCertificate(t *testing.T) { }, }, checkFunc: func(t *testing.T, cert *Certificate) { - require.False(t, cert.NodeBootstrap()) + require.False(t, cert.NodeBootstrap("system:serviceaccount:castai-agent:castai-cluster-controller")) }, wantErr: false, }, diff --git a/internal/actions/csr/svc.go b/internal/actions/csr/svc.go index ae18b3f..abbbe27 100644 --- a/internal/actions/csr/svc.go +++ b/internal/actions/csr/svc.go @@ -20,10 +20,11 @@ const ( approveCSRTimeout = 4 * time.Minute ) -func NewApprovalManager(log logrus.FieldLogger, clientset kubernetes.Interface) *ApprovalManager { +func NewApprovalManager(log logrus.FieldLogger, clientset kubernetes.Interface, clusterControllerServiceAccount string) *ApprovalManager { return &ApprovalManager{ - log: log, - clientset: clientset, + log: log, + clientset: clientset, + clusterControllerServiceAccount: clusterControllerServiceAccount, } } @@ -32,6 +33,8 @@ type ApprovalManager struct { clientset kubernetes.Interface cancelAutoApprove context.CancelFunc + clusterControllerServiceAccount string + inProgress map[string]struct{} // one handler per csr/certificate Name. m sync.Mutex // Used to make sure there is just one watcher running. } @@ -46,7 +49,7 @@ func (h *ApprovalManager) Start(ctx context.Context) error { handlerFuncs := cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { - if err := processCSREvent(ctx, c, obj); err != nil { + if err := processCSREvent(ctx, c, obj, h.clusterControllerServiceAccount); err != nil { h.log.WithError(err).Warn("failed to process csr add event") } }, diff --git a/internal/actions/csr/svc_test.go b/internal/actions/csr/svc_test.go index 4f592ac..0606034 100644 --- a/internal/actions/csr/svc_test.go +++ b/internal/actions/csr/svc_test.go @@ -76,7 +76,7 @@ func TestCSRApprove(t *testing.T) { csrName := "node-csr-123" userName := "kubelet-bootstrap" client := fake.NewClientset(getCSRv1(csrName, userName)) - s := NewApprovalManager(log, client) + s := NewApprovalManager(log, client, "system:serviceaccount:castai-agent:castai-cluster-controller") watcher := watch.NewFake() client.PrependWatchReactor("certificatesigningrequests", ktest.DefaultWatchReactor(watcher, nil)) @@ -111,7 +111,7 @@ func TestCSRApprove(t *testing.T) { csrName := "123" userName := "kubelet-bootstrap" client := fake.NewClientset(getCSRv1(csrName, userName)) - s := NewApprovalManager(log, client) + s := NewApprovalManager(log, client, "system:serviceaccount:castai-agent:castai-cluster-controller") watcher := watch.NewFake() client.PrependWatchReactor("certificatesigningrequests", ktest.DefaultWatchReactor(watcher, nil)) diff --git a/internal/config/config.go b/internal/config/config.go index e8abe84..f886c34 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "os" + "strings" "time" "github.com/sirupsen/logrus" @@ -28,6 +29,7 @@ type Config struct { MonitorMetadataPath string `mapstructure:"monitor_metadata"` SelfPod Pod `mapstructure:"self_pod"` + ServiceAccount string `mapstructure:"service_account_name"` } type Pod struct { @@ -90,6 +92,8 @@ func Get() Config { _ = viper.BindEnv("self_pod.node", "KUBERNETES_NODE_NAME") _ = viper.BindEnv("self_pod.name", "KUBERNETES_POD") _ = viper.BindEnv("self_pod.namespace", "LEADER_ELECTION_NAMESPACE") + // TODO(furkhat@cast.ai): update helm charts + _ = viper.BindEnv("service_account_name", "SERVICE_ACCOUNT") cfg = &Config{} if err := viper.Unmarshal(&cfg); err != nil { @@ -115,6 +119,12 @@ func Get() Config { required("LEADER_ELECTION_NAMESPACE") } + if !strings.HasPrefix(cfg.ServiceAccount, "system:serviceaccount:") { + cfg.ServiceAccount = "system:serviceaccount:" + cfg.SelfPod.Namespace + ":" + cfg.ServiceAccount + } else if cfg.ServiceAccount == "" { + cfg.ServiceAccount = "system:serviceaccount:castai-agent:castai-cluster-controller" + } + if cfg.LeaderElection.Enabled { if cfg.LeaderElection.LockName == "" { required("LEADER_ELECTION_LOCK_NAME") diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 4552b40..7ef2362 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -35,7 +35,8 @@ func TestConfig(t *testing.T) { SelfPod: Pod{ Namespace: "castai-agent", }, - ClusterID: "c1", + ServiceAccount: "system:serviceaccount:castai-agent:castai-cluster-controller", + ClusterID: "c1", LeaderElection: LeaderElection{ Enabled: true, LockName: "castai-cluster-controller", diff --git a/main.go b/main.go index 54282a9..bf1cc41 100644 --- a/main.go +++ b/main.go @@ -12,9 +12,9 @@ import ( // These should be set via `go build` during a release. var ( - GitCommit = "undefined" + GitCommit = "4a3f219" GitRef = "no-ref" - Version = "local" + Version = "v0.54.6" ) func main() {