diff --git a/README.md b/README.md index bc2ca39..aba7e78 100644 --- a/README.md +++ b/README.md @@ -124,8 +124,7 @@ data: ## Restarting Deployments -You can optionally restart your deployments if needed. If this isn't needed you can exclude the permissions in the role -above. +You can optionally restart your deployments if needed. Set the `RESTART_DEPLOYMENTS` environment variable to `true`. If this isn't needed you can exclude the permission in the role above and the variable. The process will list all deployments with the label `iam-role-type=aws-iam-anywhere` and restart them. diff --git a/aws_signing_helper/credentials.go b/aws_signing_helper/credentials.go index 998a2a6..3487258 100644 --- a/aws_signing_helper/credentials.go +++ b/aws_signing_helper/credentials.go @@ -5,6 +5,8 @@ import ( "encoding/base64" "errors" "github.com/aws/rolesanywhere-credential-helper/rolesanywhere" + v1 "k8s.io/api/core/v1" + v1m "k8s.io/apimachinery/pkg/apis/meta/v1" "log" "net/http" "runtime" @@ -126,3 +128,20 @@ func GenerateCredentials(opts *CredentialsOpts, signer Signer, signatureAlgorith } return credentialProcessOutput, nil } + +func (credentials CredentialProcessOutput) ToSecret(secretName string) *v1.Secret { + return &v1.Secret{ + ObjectMeta: v1m.ObjectMeta{ + Name: secretName, + Labels: map[string]string{ + "managed-by": "aws-iam-anywhere-refresher", + }, + }, + StringData: map[string]string{ + "AWS_ACCESS_KEY_ID": credentials.AccessKeyId, + "AWS_SECRET_ACCESS_KEY": credentials.SecretAccessKey, + "AWS_SESSION_TOKEN": credentials.SessionToken, + }, + } + +} diff --git a/kube_client/client.go b/kube_client/client.go new file mode 100644 index 0000000..a59913f --- /dev/null +++ b/kube_client/client.go @@ -0,0 +1,82 @@ +package kube_client + +import ( + "context" + v1a "k8s.io/api/apps/v1" + v1c "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "time" +) + +type kubeClient interface { + GetSecret(namespace string, secretName string) (*v1c.Secret, error) + CreateSecret(namespace string, secret *v1c.Secret) (*v1c.Secret, error) + UpdateSecret(namespace string, secret *v1c.Secret) (*v1c.Secret, error) + ListDeployments(namespace string) (*v1a.DeploymentList, error) + RestartDeployments(namespace string, deployments *v1a.DeploymentList) error +} + +type KubeClientImpl struct { + kubeClient + clientSet *kubernetes.Clientset +} + +func NewKubeClient() (*KubeClientImpl, error) { + + config, err := rest.InClusterConfig() + if err != nil { + return nil, err + } + + client, err := kubernetes.NewForConfig(config) + + if err != nil { + return nil, err + } + + return &KubeClientImpl{ + clientSet: client, + }, nil +} + +func (k KubeClientImpl) GetSecret(namespace string, secretName string) (*v1c.Secret, error) { + secret, err := k.clientSet.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, v1.GetOptions{}) + + if err != nil { + return nil, err + } + + return secret, nil +} + +func (k KubeClientImpl) CreateSecret(namespace string, secret *v1c.Secret) (*v1c.Secret, error) { + return k.clientSet.CoreV1().Secrets(namespace).Create(context.TODO(), secret, v1.CreateOptions{}) +} + +func (k KubeClientImpl) UpdateSecret(namespace string, secret *v1c.Secret) (*v1c.Secret, error) { + return k.clientSet.CoreV1().Secrets(namespace).Update(context.TODO(), secret, v1.UpdateOptions{}) +} + +func (k KubeClientImpl) ListDeployments(namespace string) (*v1a.DeploymentList, error) { + return k.clientSet.AppsV1().Deployments(namespace).List(context.TODO(), v1.ListOptions{ + LabelSelector: "iam-role-type=aws-iam-anywhere", + }) +} + +func (k KubeClientImpl) RestartDeployments(namespace string, deployments *v1a.DeploymentList) error { + for _, deployment := range deployments.Items { + if deployment.Spec.Template.ObjectMeta.Annotations == nil { + deployment.Spec.Template.ObjectMeta.Annotations = make(map[string]string) + } + + deployment.Spec.Template.ObjectMeta.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339) + _, err := k.clientSet.AppsV1().Deployments(namespace).Update(context.TODO(), &deployment, v1.UpdateOptions{}) + if err != nil { + return err + } + } + + return nil +} diff --git a/main.go b/main.go index 0ecc60d..4113080 100644 --- a/main.go +++ b/main.go @@ -1,31 +1,19 @@ package main import ( - "context" "encoding/base64" helper "git.s.int/rrise/aws-iam-anywhere-refresher/aws_signing_helper" "git.s.int/rrise/aws-iam-anywhere-refresher/cmd" appConfig "git.s.int/rrise/aws-iam-anywhere-refresher/config" - v1k "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" + "git.s.int/rrise/aws-iam-anywhere-refresher/kube_client" "log" "os" - "time" ) func main() { println("Starting credentials refresh") - config, err := rest.InClusterConfig() - if err != nil { - println("Are you running in a cluster?") - panic(err) - } - - client, err := kubernetes.NewForConfig(config) - + client, err := kube_client.NewKubeClient() if err != nil { panic(err) } @@ -60,65 +48,35 @@ func main() { println("Got new credentials") - secret := &v1k.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: c.Secret(), - Labels: map[string]string{ - "managed-by": "aws-iam-anywhere-refresher", - }, - }, - StringData: map[string]string{ - "AWS_ACCESS_KEY_ID": credentials.AccessKeyId, - "AWS_SECRET_ACCESS_KEY": credentials.SecretAccessKey, - "AWS_SESSION_TOKEN": credentials.SessionToken, - }, - } - - _, err = client.CoreV1().Secrets(c.Namespace()).Get(context.TODO(), c.Secret(), v1.GetOptions{}) + _, err = client.GetSecret(c.Namespace(), c.Secret()) if err != nil { println(err.Error()) println("secret doesn't exist, trying to create") - - create, err := client.CoreV1().Secrets(c.Namespace()).Create(context.Background(), secret, v1.CreateOptions{}) + create, err := client.CreateSecret(c.Namespace(), credentials.ToSecret(c.Secret())) if err != nil { panic(err) } - println("secret created") println(create.CreationTimestamp.String()) - } else { - update, err := client.CoreV1().Secrets(c.Namespace()).Update(context.TODO(), secret, v1.UpdateOptions{}) + update, err := client.UpdateSecret(c.Namespace(), credentials.ToSecret(c.Secret())) if err != nil { panic(err) } - println("secret updated") println(update.CreationTimestamp.String()) } if c.RestartDeployments() { println("Restarting deployments...") - - deployments, err := client.AppsV1().Deployments(c.Namespace()).List(context.TODO(), v1.ListOptions{ - LabelSelector: "iam-role-type=aws-iam-anywhere", - }) - + deployments, err := client.ListDeployments(c.Namespace()) if err != nil { panic(err) } - for _, deployment := range deployments.Items { - println("Restarting deployment", deployment.Name) - - if deployment.Spec.Template.ObjectMeta.Annotations == nil { - deployment.Spec.Template.ObjectMeta.Annotations = make(map[string]string) - } - deployment.Spec.Template.ObjectMeta.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339) - _, err = client.AppsV1().Deployments(c.Namespace()).Update(context.TODO(), &deployment, v1.UpdateOptions{}) - if err != nil { - println(err.Error()) - } + err = client.RestartDeployments(c.Namespace(), deployments) + if err != nil { + panic(err) } }