diff --git a/.gitignore b/.gitignore index 567609b..e9418d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build/ +vendor/ diff --git a/Makefile b/Makefile index 9540d85..318c0d5 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ VERSION ?= $(shell git describe --tags --always --dirty) IMAGE ?= mikkeloscar/$(BINARY) TAG ?= $(VERSION) SOURCES = $(shell find . -name '*.go') +GENERATED = pkg/client pkg/apis/amazonaws.com/v1/zz_generated.deepcopy.go DOCKERFILE ?= Dockerfile GOPKGS = $(shell go list ./...) BUILD_FLAGS ?= -v @@ -14,25 +15,29 @@ default: build.local clean: rm -rf build + rm -rf $(GENERATED) -test: go.mod +test: go.mod $(GENERATED) go test -v $(GOPKGS) -check: go.mod +check: go.mod $(GENERATED) golint $(GOPKGS) go vet -v $(GOPKGS) +$(GENERATED): + ./hack/update-codegen.sh + build.local: build/$(BINARY) build.linux: build/linux/$(BINARY) build.osx: build/osx/$(BINARY) -build/$(BINARY): go.mod $(SOURCES) +build/$(BINARY): go.mod $(GENERATED) $(SOURCES) CGO_ENABLED=0 go build -o build/$(BINARY) $(BUILD_FLAGS) -ldflags "$(LDFLAGS)" . -build/linux/$(BINARY): go.mod $(SOURCES) +build/linux/$(BINARY): go.mod $(GENERATED) $(SOURCES) GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build $(BUILD_FLAGS) -o build/linux/$(BINARY) -ldflags "$(LDFLAGS)" . -build/osx/$(BINARY): go.mod $(SOURCES) +build/osx/$(BINARY): go.mod $(GENERATED) $(SOURCES) GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build $(BUILD_FLAGS) -o build/osx/$(BINARY) -ldflags "$(LDFLAGS)" . build.docker: build.linux diff --git a/README.md b/README.md index 201d234..5478862 100644 --- a/README.md +++ b/README.md @@ -57,30 +57,46 @@ See the [configuration guide for supported SDKs](/docs/sdk-configuration.md). ## How it works -The controller watches for new pods, if it sees a pod which has an AWS IAM role -defined it will ensure there is a secret containing credentials for the IAM -role which can be mounted as a file by the pod. +The controller continuously looks for custom `AWSIAMRole` resources which +specify an AWS IAM role by name or by the full ARN. For each resource it finds, +it will generate/update corresponding secrets containing credentialds for the +IAM role specified. +The secrets can be mounted by pods as a file enabling the +AWS SDKs to use the credentials. -Each secret resource created by the controller will have a label -`heritage=kube-aws-iam-controller` to indicate that it's owned by the -controller. -The controller will continuously pull all secrets with this label and ensure -the credentials are refreshed before they expire. It will also cleanup secrets -with credentials which are no longer requested by any pods. +If an `AWSIAMRole` resource is deleted, the corresponding secret would be +automatically cleaned up as well. ### Specifying AWS IAM role on pods **See the [configuration guide for supported SDKs](/docs/sdk-configuration.md)**. -In order to specify that a pod should get a certain AWS IAM role assigned the -pod spec must include a volume mount from a secret with the following secret -name pattern: `aws-iam-`. Further more a volume mount point -must be defined for each container requiring the role in the pod and each -container must also have the environment variable -`AWS_SHARED_CREDENTIALS_FILE=/path/to/mounted/secret` defined. The environment -variable is used by AWS SDKs and the AWS CLI to automatically find and use the -credentials file. +In order to specify that a certain AWS IAM Role should be available for +applications in a namespace you need to define an `AWSIAMRole` resource which +references the IAM role you want: + +```yaml +apiVersion: amazonaws.com/v1 +kind: AWSIAMRole +metadata: + name: my-app-iam-role +spec: + # The roleReference allows specifying an AWS IAM role name or arn + # Possible values: + # "aws-iam-role-name" + # "arn:aws:iam:::role/aws-iam-role-name" + roleReference: +``` + +The controller will detect the resource and create a corresponding secret with +the same name containing the role credentials. To use the credentials in a pod +you simply mount the secret (called `my-app-iam-role` in this example), making +the credentials available as a file for your application to read and use. +Additionally you must also define an environment variable +`AWS_SHARED_CREDENTIALS_FILE=/path/to/mounted/secret` for each container. The +environment variable is used by AWS SDKs and the AWS CLI to automatically find +and use the credentials file. See a full example in [example-app.yaml](/docs/example-app.yaml). @@ -171,7 +187,7 @@ initial credentials and create a secret. ```sh $ export ARN="arn.of.the.iam.role" -$ kubectl create secret generic aws-iam- --from-literal "credentials.json=$(./scripts/get_credentials.sh "$ARN")" --from-literal "credentials.process=$(printf "[default]\ncredential_process = cat /meta/aws-iam/credentials.json\n")" +$ kubectl create secret generic kube-aws-iam-controller-iam-role --from-literal "credentials.json=$(./scripts/get_credentials.sh "$ARN")" --from-literal "credentials.process=$(printf "[default]\ncredential_process = cat /meta/aws-iam/credentials.json\n")" ``` Once the secret is created you can deploy the controller using the example diff --git a/awsiamrole_controller.go b/awsiamrole_controller.go new file mode 100644 index 0000000..d90a54e --- /dev/null +++ b/awsiamrole_controller.go @@ -0,0 +1,443 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "time" + + av1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + "github.com/mikkeloscar/kube-aws-iam-controller/pkg/clientset" + "github.com/mikkeloscar/kube-aws-iam-controller/pkg/recorder" + log "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/record" +) + +const ( + awsIAMRoleGenerationKey = "awsiamrole-generation" +) + +var ( + awsIAMRoleOwnerLabels = map[string]string{ + heritageLabelKey: awsIAMControllerLabelValue, + "type": "awsiamrole", + } +) + +// AWSIAMRoleController is a controller which lists AWSIAMRole resources and +// create/update matching secrets with AWS IAM role credentials. +type AWSIAMRoleController struct { + client clientset.Interface + recorder record.EventRecorder + interval time.Duration + refreshLimit time.Duration + creds CredentialsGetter + namespace string +} + +// NewSecretsController initializes a new AWSIAMRoleController. +func NewAWSIAMRoleController(client clientset.Interface, interval, refreshLimit time.Duration, creds CredentialsGetter, namespace string) *AWSIAMRoleController { + return &AWSIAMRoleController{ + client: client, + recorder: recorder.CreateEventRecorder(client), + interval: interval, + refreshLimit: refreshLimit, + creds: creds, + namespace: namespace, + } +} + +// getCreds gets new credentials from the CredentialsGetter and converts them +// to a secret data map. +func (c *AWSIAMRoleController) getCreds(role string, sessionDuration time.Duration) (*Credentials, map[string][]byte, error) { + creds, err := c.creds.Get(role, sessionDuration) + if err != nil { + return nil, nil, err + } + + credsFile := fmt.Sprintf( + credentialsFileTemplate, + creds.AccessKeyID, + creds.SecretAccessKey, + creds.SessionToken, + creds.Expiration.Format(time.RFC3339), + ) + + processCreds := ProcessCredentials{ + Version: 1, + AccessKeyID: creds.AccessKeyID, + SecretAccessKey: creds.SecretAccessKey, + SessionToken: creds.SessionToken, + Expiration: creds.Expiration, + } + + processCredsData, err := json.Marshal(&processCreds) + if err != nil { + return nil, nil, err + } + + return creds, map[string][]byte{ + roleARNKey: []byte(creds.RoleARN), + expireKey: []byte(creds.Expiration.Format(time.RFC3339)), + credentialsFileKey: []byte(credsFile), + credentialsProcessFileKey: []byte(credentialsProcessFileContent), + credentialsJSONFileKey: processCredsData, + }, nil +} + +// Run runs the secret controller loop. This will refresh secrets with AWS IAM +// roles. +func (c *AWSIAMRoleController) Run(ctx context.Context) { + for { + err := c.refresh() + if err != nil { + log.Error(err) + } + + select { + case <-time.After(c.interval): + case <-ctx.Done(): + log.Info("Terminating AWSIAMRole controller loop.") + return + } + } +} + +// refresh checks for soon to expire secrets and requests new credentials. It +// also looks for AWSIAMRole resources where secrets are missing and creates +// the secrets for the designated namespace. +func (c *AWSIAMRoleController) refresh() error { + opts := metav1.ListOptions{ + LabelSelector: labels.Set(awsIAMRoleOwnerLabels).AsSelector().String(), + } + + secrets, err := c.client.CoreV1().Secrets(c.namespace).List(opts) + if err != nil { + return err + } + + awsIAMRoles, err := c.client.AmazonawsV1().AWSIAMRoles(c.namespace).List(metav1.ListOptions{}) + if err != nil { + return err + } + + secretsMap := make(map[string]v1.Secret, len(secrets.Items)) + for _, secret := range secrets.Items { + secretsMap[secret.Namespace+"/"+secret.Name] = secret + } + + awsIAMRolesMap := make(map[string]av1.AWSIAMRole, len(awsIAMRoles.Items)) + for _, role := range awsIAMRoles.Items { + awsIAMRolesMap[role.Namespace+"/"+role.Name] = role + } + + orphanSecrets := make([]v1.Secret, 0, len(secrets.Items)) + for _, secret := range secrets.Items { + awsIAMRole, ok := awsIAMRolesMap[secret.Namespace+"/"+secret.Name] + if !ok || !isOwnedReference(awsIAMRole.TypeMeta, awsIAMRole.ObjectMeta, secret.ObjectMeta) { + orphanSecrets = append(orphanSecrets, secret) + continue + } + + role := awsIAMRole.Spec.RoleReference + roleSessionDuration := 3600 * time.Second + if awsIAMRole.Spec.RoleSessionDuration > 0 { + roleSessionDuration = time.Duration(awsIAMRole.Spec.RoleSessionDuration) * time.Second + } + + // TODO: move to function + refreshCreds := false + + expirary, ok := secret.Data[expireKey] + if !ok { + refreshCreds = true + } else { + expire, err := time.Parse(time.RFC3339, string(expirary)) + if err != nil { + log.Debugf("Failed to parse expirary time %s: %v", expirary, err) + refreshCreds = true + } else if time.Now().UTC().Add(c.refreshLimit).After(expire) { + refreshCreds = true + } + } + + if refreshCreds { + var creds *Credentials + creds, secret.Data, err = c.getCreds(role, roleSessionDuration) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "GetCredentialsFailed", + fmt.Sprintf("Failed to get creedentials for role '%s': %v", role, err), + ) + continue + } + + // update secret labels + secret.Labels = mergeLabels(awsIAMRole.Labels, awsIAMRoleOwnerLabels) + secret.Data[awsIAMRoleGenerationKey] = []byte(fmt.Sprintf("%d", awsIAMRole.Generation)) + + // update secret with refreshed credentials + _, err := c.client.CoreV1().Secrets(secret.Namespace).Update(&secret) + if err != nil { + log.Errorf("Failed to update secret %s/%s: %v", secret.Namespace, secret.Name, err) + continue + } + + log.WithFields(log.Fields{ + "action": "update", + "role-arn": creds.RoleARN, + "secret": secret.Name, + "namespace": secret.Namespace, + "expire": creds.Expiration.String(), + "type": "awsiamrole", + }).Info() + c.recorder.Event(&awsIAMRole, + v1.EventTypeNormal, + "UpdateCredentials", + fmt.Sprintf("Updated credentials for role '%s', expiry time: %s", creds.RoleARN, creds.Expiration.String()), + ) + + // update AWSIAMRole status + expiryTime := metav1.NewTime(creds.Expiration) + awsIAMRole.Status = av1.AWSIAMRoleStatus{ + ObservedGeneration: &awsIAMRole.Generation, + RoleARN: creds.RoleARN, + Expiration: &expiryTime, + } + + _, err = c.client.AmazonawsV1().AWSIAMRoles(awsIAMRole.Namespace).UpdateStatus(&awsIAMRole) + if err != nil { + log.Errorf("Failed to update status for AWSIAMRole %s/%s: %v", awsIAMRole.Namespace, awsIAMRole.Name, err) + continue + } + } + } + + // clean up orphaned secrets + for _, secret := range orphanSecrets { + err := c.client.CoreV1().Secrets(secret.Namespace).Delete(secret.Name, nil) + if err != nil { + log.Errorf("Failed to delete secret %s/%s: %v", secret.Namespace, secret.Name, err) + continue + } + + log.WithFields(log.Fields{ + "action": "delete", + "role-arn": string(secret.Data[roleARNKey]), + "secret": secret.Name, + "namespace": secret.Namespace, + }).Info("Removing unused credentials") + continue + } + + // create secrets for new AWSIAMRoles without a secret + for _, awsIAMRole := range awsIAMRoles.Items { + role := awsIAMRole.Spec.RoleReference + roleSessionDuration := 3600 * time.Second + if awsIAMRole.Spec.RoleSessionDuration > 0 { + roleSessionDuration = time.Duration(awsIAMRole.Spec.RoleSessionDuration) * time.Second + } + + if secret, ok := secretsMap[awsIAMRole.Namespace+"/"+awsIAMRole.Name]; ok { + // update secret if out of date + generation, err := getGeneration(secret.Data) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "ReadSecretFailed", + fmt.Sprintf("Failed to parse AWSIAMRole generation from secret %s/%s: %v", + secret.Namespace, + secret.Name, + err), + ) + continue + } + + if awsIAMRole.Generation != generation { + var creds *Credentials + creds, secret.Data, err = c.getCreds(role, roleSessionDuration) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "GetCredentialsFailed", + fmt.Sprintf("Failed to get creedentials for role '%s': %v", role, err), + ) + continue + } + + // update secret labels + secret.Labels = mergeLabels(awsIAMRole.Labels, awsIAMRoleOwnerLabels) + secret.Data[awsIAMRoleGenerationKey] = []byte(fmt.Sprintf("%d", awsIAMRole.Generation)) + + // update secret with refreshed credentials + _, err := c.client.CoreV1().Secrets(secret.Namespace).Update(&secret) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "UpdateSecretFailed", + fmt.Sprintf("Failed to update secret %s/%s with credentials: %v", secret.Namespace, secret.Name, err), + ) + continue + } + + log.WithFields(log.Fields{ + "action": "update", + "role-arn": creds.RoleARN, + "secret": secret.Name, + "namespace": secret.Namespace, + "expire": creds.Expiration.String(), + "type": "awsiamrole", + }).Info() + c.recorder.Event(&awsIAMRole, + v1.EventTypeNormal, + "UpdateCredentials", + fmt.Sprintf("Updated credentials for role '%s', expiry time: %s", creds.RoleARN, creds.Expiration.String()), + ) + } + + // update AWSIAMRole status if not up to date + if awsIAMRole.Status.ObservedGeneration == nil || *awsIAMRole.Status.ObservedGeneration != awsIAMRole.Generation { + expiration, err := time.Parse(time.RFC3339, string(secret.Data[expireKey])) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "ReadSecretFailed", + fmt.Sprintf("Failed to parse expirary time '%s' from secret %s/%s: %v", + string(secret.Data[expireKey]), + secret.Namespace, + secret.Name, + err), + ) + continue + } + expiryTime := metav1.NewTime(expiration) + awsIAMRole.Status = av1.AWSIAMRoleStatus{ + ObservedGeneration: &awsIAMRole.Generation, + RoleARN: string(secret.Data[roleARNKey]), + Expiration: &expiryTime, + } + + // update AWSIAMRole status + _, err = c.client.AmazonawsV1().AWSIAMRoles(awsIAMRole.Namespace).UpdateStatus(&awsIAMRole) + if err != nil { + log.Errorf("Failed to update status of AWSIAMRole %s/%s: %v", awsIAMRole.Namespace, awsIAMRole.Name, err) + continue + } + } + continue + } + + creds, secretData, err := c.getCreds(role, roleSessionDuration) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "GetCredentialsFailed", + fmt.Sprintf("Failed to get creedentials for role '%s': %v", role, err), + ) + continue + } + + secretData[awsIAMRoleGenerationKey] = []byte(fmt.Sprintf("%d", awsIAMRole.Generation)) + + // create secret + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: awsIAMRole.Name, + Namespace: awsIAMRole.Namespace, + Labels: mergeLabels(awsIAMRole.Labels, awsIAMRoleOwnerLabels), + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: awsIAMRole.APIVersion, + Kind: awsIAMRole.Kind, + Name: awsIAMRole.Name, + UID: awsIAMRole.UID, + }, + }, + }, + Data: secretData, + } + + _, err = c.client.CoreV1().Secrets(awsIAMRole.Namespace).Create(secret) + if err != nil { + c.recorder.Event(&awsIAMRole, + v1.EventTypeWarning, + "CreateSecretFailed", + fmt.Sprintf("Failed to create secret %s/%s with credentials: %v", awsIAMRole.Namespace, awsIAMRole.Name, err), + ) + continue + } + log.WithFields(log.Fields{ + "action": "create", + "role-arn": creds.RoleARN, + "secret": awsIAMRole.Name, + "namespace": awsIAMRole.Namespace, + "expire": creds.Expiration.String(), + "type": "awsiamrole", + }).Info() + c.recorder.Event(&awsIAMRole, + v1.EventTypeNormal, + "CreateCredentials", + fmt.Sprintf("Created credentials for role '%s', expiry time: %s", creds.RoleARN, creds.Expiration.String()), + ) + + // update AWSIAMRole status + expiryTime := metav1.NewTime(creds.Expiration) + awsIAMRole.Status = av1.AWSIAMRoleStatus{ + ObservedGeneration: &awsIAMRole.Generation, + RoleARN: creds.RoleARN, + Expiration: &expiryTime, + } + + _, err = c.client.AmazonawsV1().AWSIAMRoles(awsIAMRole.Namespace).UpdateStatus(&awsIAMRole) + if err != nil { + log.Errorf("Failed to update status of AWSIAMRole %s/%s: %v", awsIAMRole.Namespace, awsIAMRole.Name, err) + continue + } + } + + return nil +} + +// isOwnedReference returns true of the dependent object is owned by the owner +// object. +func isOwnedReference(ownerTypeMeta metav1.TypeMeta, ownerObjectMeta, dependent metav1.ObjectMeta) bool { + for _, ref := range dependent.OwnerReferences { + if ref.APIVersion == ownerTypeMeta.APIVersion && + ref.Kind == ownerTypeMeta.Kind && + ref.UID == ownerObjectMeta.UID && + ref.Name == ownerObjectMeta.Name { + return true + } + } + return false +} + +func mergeLabels(base, additional map[string]string) map[string]string { + labels := make(map[string]string, len(base)+len(additional)) + for k, v := range base { + labels[k] = v + } + + for k, v := range additional { + labels[k] = v + } + return labels +} + +func getGeneration(secretData map[string][]byte) (int64, error) { + generation := int64(0) + gen, ok := secretData[awsIAMRoleGenerationKey] + if ok { + i, err := strconv.ParseInt(string(gen), 10, 64) + if err != nil { + return 0, err + } + generation = i + } + return generation, nil +} diff --git a/awsiamrole_controller_test.go b/awsiamrole_controller_test.go new file mode 100644 index 0000000..93a8551 --- /dev/null +++ b/awsiamrole_controller_test.go @@ -0,0 +1,173 @@ +package main + +import ( + "testing" + "time" + + av1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + fakeAWS "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/fake" + "github.com/mikkeloscar/kube-aws-iam-controller/pkg/clientset" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + fakeKube "k8s.io/client-go/kubernetes/fake" +) + +func TestIsOwnedReference(t *testing.T) { + owner := av1.AWSIAMRole{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "amazonaws.com/v1", + Kind: "AWSIAMRole", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test1", + UID: types.UID("1234"), + }, + } + + dependent := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: owner.Name, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: owner.APIVersion, + Kind: owner.Kind, + Name: owner.Name, + UID: owner.UID, + }, + }, + }, + } + + require.True(t, isOwnedReference(owner.TypeMeta, owner.ObjectMeta, dependent.ObjectMeta)) + + dependent.OwnerReferences[0].UID = types.UID("4321") + require.False(t, isOwnedReference(owner.TypeMeta, owner.ObjectMeta, dependent.ObjectMeta)) +} + +func TestRefreshAWSIAMRole(tt *testing.T) { + for _, tc := range []struct { + msg string + secrets []v1.Secret + awsIAMRoles []av1.AWSIAMRole + credsGetter CredentialsGetter + expectedSecrets []v1.Secret + }{ + { + msg: "", + credsGetter: &mockCredsGetter{ + creds: &Credentials{ + RoleARN: "arn", + }, + }, + secrets: []v1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "non-expired", + Labels: awsIAMRoleOwnerLabels, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "non-expired", + }, + }, + }, + Data: map[string][]byte{ + expireKey: []byte(time.Now().UTC().Add(1 * time.Hour).Format(time.RFC3339)), + roleARNKey: []byte("arn"), + awsIAMRoleGenerationKey: []byte("1"), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "expired", + Labels: awsIAMRoleOwnerLabels, + OwnerReferences: []metav1.OwnerReference{ + { + Name: "expired", + }, + }, + }, + Data: map[string][]byte{ + expireKey: []byte(time.Now().UTC().Add(-1 * time.Minute).Format(time.RFC3339)), + roleARNKey: []byte("arn"), + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "orphan", + Labels: awsIAMRoleOwnerLabels, + }, + }, + }, + awsIAMRoles: []av1.AWSIAMRole{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "non-expired", + Generation: 2, + }, + Spec: av1.AWSIAMRoleSpec{ + RoleReference: "arn", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "expired", + }, + Spec: av1.AWSIAMRoleSpec{ + RoleReference: "arn", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "new", + }, + Spec: av1.AWSIAMRoleSpec{ + RoleReference: "arn", + }, + }, + }, + expectedSecrets: []v1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "non-expired", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "expired", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "new", + }, + }, + }, + }, + } { + tt.Run(tc.msg, func(t *testing.T) { + awsKubeClient := fakeAWS.NewSimpleClientset() + kubeClient := fakeKube.NewSimpleClientset() + client := clientset.NewClientset(kubeClient, awsKubeClient) + + for _, role := range tc.awsIAMRoles { + _, err := client.AmazonawsV1().AWSIAMRoles("default").Create(&role) + require.NoError(t, err) + } + + for _, secret := range tc.secrets { + _, err := client.CoreV1().Secrets("default").Create(&secret) + require.NoError(t, err) + } + + controller := NewAWSIAMRoleController(client, 0, 15*time.Minute, tc.credsGetter, "default") + err := controller.refresh() + require.NoError(t, err) + + secrets, err := client.CoreV1().Secrets("default").List(metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, secrets.Items, len(tc.expectedSecrets)) + }) + } +} diff --git a/credentials_getter.go b/credentials_getter.go index 9c6b36d..a43679b 100644 --- a/credentials_getter.go +++ b/credentials_getter.go @@ -12,20 +12,22 @@ import ( "github.com/aws/aws-sdk-go/service/sts/stsiface" ) -// TODO: Assume role ttl +const ( + arnPrefix = "arn:aws:iam::" +) // CredentialsGetter can get credentials. type CredentialsGetter interface { - Get(role string) (*Credentials, error) + Get(role string, sessionDuration time.Duration) (*Credentials, error) } // Credentials defines fecthed credentials including expiration time. type Credentials struct { - Role string + RoleARN string AccessKeyID string SecretAccessKey string SessionToken string - Expiration *time.Time + Expiration time.Time } // STSCredentialsGetter is a credentials getter for getting credentials from @@ -45,13 +47,17 @@ func NewSTSCredentialsGetter(sess *session.Session, baseRoleARN string) *STSCred // Get gets new credentials for the specified role. The credentials are fetched // via STS. -func (c *STSCredentialsGetter) Get(role string) (*Credentials, error) { +func (c *STSCredentialsGetter) Get(role string, sessionDuration time.Duration) (*Credentials, error) { roleARN := c.baseRoleARN + role - roleSessionName := role + "-session" + if strings.HasPrefix(role, arnPrefix) { + roleARN = role + } + roleSessionName := normalizeRoleARN(roleARN) + "-session" params := &sts.AssumeRoleInput{ RoleArn: aws.String(roleARN), RoleSessionName: aws.String(roleSessionName), + DurationSeconds: aws.Int64(int64(sessionDuration.Seconds())), } resp, err := c.svc.AssumeRole(params) @@ -60,10 +66,11 @@ func (c *STSCredentialsGetter) Get(role string) (*Credentials, error) { } return &Credentials{ + RoleARN: roleARN, AccessKeyID: aws.StringValue(resp.Credentials.AccessKeyId), SecretAccessKey: aws.StringValue(resp.Credentials.SecretAccessKey), SessionToken: aws.StringValue(resp.Credentials.SessionToken), - Expiration: resp.Credentials.Expiration, + Expiration: aws.TimeValue(resp.Credentials.Expiration), }, nil } @@ -84,3 +91,12 @@ func GetBaseRoleARN(sess *session.Session) (string, error) { return fmt.Sprintf("%s/", baseRoleARN[0]), nil } + +// normalizeRoleARN normalizes a role ARN by substituting special characters +// with characters allowed for a RoleSessionName according to: +// https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html +func normalizeRoleARN(roleARN string) string { + roleARN = strings.Replace(roleARN, ":", "_", -1) + roleARN = strings.Replace(roleARN, "/", ".", -1) + return roleARN +} diff --git a/credentials_getter_test.go b/credentials_getter_test.go index e3fc80b..9474a50 100644 --- a/credentials_getter_test.go +++ b/credentials_getter_test.go @@ -40,17 +40,17 @@ func TestGet(t *testing.T) { }, } - creds, err := getter.Get("role") + creds, err := getter.Get("role", 3600*time.Second) require.NoError(t, err) require.Equal(t, "access_key_id", creds.AccessKeyID) require.Equal(t, "secret_access_key", creds.SecretAccessKey) require.Equal(t, "session_token", creds.SessionToken) - require.Equal(t, &time.Time{}, creds.Expiration) + require.Equal(t, time.Time{}, creds.Expiration) getter.svc = &mockSTSAPI{ err: errors.New("failed"), } - _, err = getter.Get("role") + _, err = getter.Get(arnPrefix+"role", 3600*time.Second) require.Error(t, err) } diff --git a/docs/aws_iam_role_crd.yaml b/docs/aws_iam_role_crd.yaml new file mode 100644 index 0000000..42b8fb8 --- /dev/null +++ b/docs/aws_iam_role_crd.yaml @@ -0,0 +1,110 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: awsiamroles.amazonaws.com +spec: + group: amazonaws.com + version: v1 + scope: Namespaced + names: + kind: AWSIAMRole + singular: awsiamrole + plural: awsiamroles + categories: + - all + additionalPrinterColumns: + - name: RoleARN + type: string + description: Full RoleARN + JSONPath: .status.roleARN + - name: Expiration + type: string + description: Expiration time of the current credentials provisioned for the role + JSONPath: .status.expiration + subresources: + # status enables the status subresource. + status: {} + # validation depends on Kubernetes >= v1.11.0 + validation: + openAPIV3Schema: + properties: + spec: + properties: + roleReference: + description: | + Reference to an AWS IAM role which can either be a role name + or a full IAM role ARN. + type: string + minLength: 3 + roleSessionDuration: + description: | + Specify the role session duration in seconds. Defaults to 3600 + seconds (1 hour). This value must be less than or equal to the + `MaxSessionDuration` value of the IAM role. + type: integer + minimum: 900 # 15 minutes + maximum: 43200 # 12 hours + roleDefinition: + properties: + AssumeRolePolicyDocument: + properties: + Statement: + type: array + items: + properties: + Action: + type: string + Effect: + type: string + Principal: + properties: + Service: + type: string + ManagedPolicyArns: + type: array + items: + type: string + # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-maxsessionduration + MaxSessionDuration: + type: integer + minimum: 3600 # 1 hour + maximum: 43200 # 12 hours + Path: + type: string + PermissionsBoundary: + type: string + Policies: + properties: + PolicyName: + type: string + PolicyDocument: + properties: + Version: + type: string + enum: ["2012-10-17"] + Statement: + type: array + items: + properties: + Effect: + type: string + enum: ["Allow", "Deny"] + Action: + type: array + items: + type: string + Resource: + type: string + required: + - PolicyName + - PolicyDocument + status: + properties: + observedGeneration: + type: integer + roleARN: + type: string + expiration: + type: string + required: + - spec diff --git a/docs/deployment.yaml b/docs/deployment.yaml index ddb562b..6659114 100644 --- a/docs/deployment.yaml +++ b/docs/deployment.yaml @@ -17,9 +17,7 @@ spec: application: kube-aws-iam-controller version: latest spec: - tolerations: - - key: CriticalAddonsOnly - operator: Exists + serviceAccount: kube-aws-iam-controller # running with hostNetwork to bypass metadata service block from pod # network. hostNetwork: true @@ -28,8 +26,8 @@ spec: image: mikkeloscar/kube-aws-iam-controller:latest resources: limits: - cpu: 200m - memory: 1Gi + cpu: 25m + memory: 100mi requests: - cpu: 100m - memory: 512Mi + cpu: 25m + memory: 100Mi diff --git a/docs/deployment_with_role.yaml b/docs/deployment_with_role.yaml index d0120e3..89e412e 100644 --- a/docs/deployment_with_role.yaml +++ b/docs/deployment_with_role.yaml @@ -17,9 +17,7 @@ spec: application: kube-aws-iam-controller version: latest spec: - tolerations: - - key: CriticalAddonsOnly - operator: Exists + serviceAccount: kube-aws-iam-controller containers: - name: kube-aws-iam-controller image: mikkeloscar/kube-aws-iam-controller:latest @@ -33,13 +31,19 @@ spec: readOnly: true resources: limits: - cpu: 200m - memory: 1Gi + cpu: 25m + memory: 100Mi requests: - cpu: 100m - memory: 512Mi + cpu: 25m + memory: 100Mi volumes: - name: aws-iam-credentials secret: - # secret should be named: aws-iam- - secretName: aws-iam- + secretName: kube-aws-iam-controller-iam-role # name of the AWSIAMRole resource +--- +apiVersion: amazonaws.com/v1 +kind: AWSIAMRole +metadata: + name: kube-aws-iam-controller-iam-role +spec: + roleReference: # AWS IAM role name or full ARN diff --git a/docs/example-app.yaml b/docs/example-app.yaml index 0555067..93b8b07 100644 --- a/docs/example-app.yaml +++ b/docs/example-app.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: aws-iam-example @@ -23,7 +23,7 @@ spec: env: # must be set for the AWS SDK/AWS CLI to find the credentials file. - name: AWS_SHARED_CREDENTIALS_FILE - value: /meta/aws-iam/credentials + value: /meta/aws-iam/credentials.process - name: S3_BUCKET value: args: @@ -37,13 +37,19 @@ spec: readOnly: true resources: limits: - cpu: 200m - memory: 1Gi + cpu: 100m + memory: 512Mi requests: cpu: 100m memory: 512Mi volumes: - name: aws-iam-credentials secret: - # secret should be named: aws-iam- - secretName: aws-iam- + secretName: my-app-iam-role # name of the AWSIAMRole resource +--- +apiVersion: amazonaws.com/v1 +kind: AWSIAMRole +metadata: + name: my-app-iam-role +spec: + roleReference: # AWS IAM role name or full ARN diff --git a/docs/rbac.yaml b/docs/rbac.yaml new file mode 100644 index 0000000..99a46e0 --- /dev/null +++ b/docs/rbac.yaml @@ -0,0 +1,63 @@ +kind: ServiceAccount +apiVersion: v1 +metadata: + name: kube-aws-iam-controller + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kube-aws-iam-controller +rules: +- apiGroups: + - "amazonaws.com" + resources: + - awsiamroles + - awsiamroles/status + verbs: + - get + - list + - watch + - update + - patch +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - watch + - list +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create + - update + - patch + - delete + - watch + - list +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kube-aws-iam-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kube-aws-iam-controller +subjects: +- kind: ServiceAccount + name: kube-aws-iam-controller + namespace: kube-system diff --git a/docs/sdk-configuration.md b/docs/sdk-configuration.md index 27c2a15..3a7770d 100644 --- a/docs/sdk-configuration.md +++ b/docs/sdk-configuration.md @@ -53,14 +53,20 @@ spec: volumes: - name: aws-iam-credentials secret: - # secret should be named: aws-iam- - secretName: aws-iam-aws-iam-example + secretName: aws-iam-java-example # name of the AWSIAMRole resource +--- +apiVersion: amazonaws.com/v1 +kind: AWSIAMRole +metadata: + name: aws-iam-java-example +spec: + roleReference: aws-iam-example ``` It's important that you set the `AWS_CREDENTIALS_PROFILES_FILE` environment -variable as shown in the example as well as mount the secret named -`aws-iam-` into the pod. This secret will be provisioned -by the **kube-aws-iam-controller**. +variable as shown in the example as well as mounting the secret named after the +`AWSIAMRole` resource into the pod under `/meta/aws-iam`. This secret will be +provisioned by the **kube-aws-iam-controller**. See full [Java example project](https://github.com/mikkeloscar/kube-aws-iam-controller-java-example). @@ -104,13 +110,19 @@ spec: volumes: - name: aws-iam-credentials secret: - # secret should be named: aws-iam- - secretName: aws-iam-aws-iam-example + secretName: aws-iam-python-example # name of the AWSIAMRole resource +--- +apiVersion: amazonaws.com/v1 +kind: AWSIAMRole +metadata: + name: aws-iam-python-example +spec: + roleReference: aws-iam-example ``` It's important that you set the `AWS_SHARED_CREDENTIALS_FILE` environment -variable as shown in the example as well as mount the secret named -`aws-iam-` into the pod under `/meta/aws-iam`. This +variable as shown in the example as well as mounting the secret named after the +`AWSIAMRole` resource into the pod under `/meta/aws-iam`. This secret will be provisioned by the **kube-aws-iam-controller**. Also note that for this to work the docker image you use **MUST** contain the @@ -167,14 +179,20 @@ spec: volumes: - name: aws-iam-credentials secret: - # secret should be named: aws-iam- - secretName: aws-iam-aws-iam-example + secretName: aws-iam-golang-example # name of the AWSIAMRole resource +--- +apiVersion: amazonaws.com/v1 +kind: AWSIAMRole +metadata: + name: aws-iam-golang-example +spec: + roleReference: aws-iam-example ``` It's important that you set the `AWS_SHARED_CREDENTIALS_FILE` environment -variable as shown in the example as well as mount the secret named -`aws-iam-` into the pod under `/meta/aws-iam`. This -secret will be provisioned by the **kube-aws-iam-controller**. +variable as shown in the example as well as mounting the secret named after the +`AWSIAMRole` resource into the pod under `/meta/aws-iam`. This secret will be +provisioned by the **kube-aws-iam-controller**. Also note that for this to work the docker image you use **MUST** contain the program `cat`. [`cat` is called by the SDK to read the credentials from a diff --git a/go.mod b/go.mod index f78e633..3ab756e 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,18 @@ require ( github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect github.com/aws/aws-sdk-go v1.16.6 - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/evanphx/json-patch v4.1.0+incompatible // indirect github.com/fsnotify/fsnotify v1.4.7 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/gogo/protobuf v1.0.0 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/gogo/protobuf v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect - github.com/golang/protobuf v1.0.0 // indirect github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a // indirect github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect - github.com/googleapis/gnostic v0.1.0 // indirect + github.com/googleapis/gnostic v0.2.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 // indirect + github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/hpcloud/tail v1.0.0 // indirect - github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece // indirect + github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 // indirect github.com/onsi/ginkgo v1.6.0 // indirect @@ -25,13 +23,10 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.0.5 - github.com/spf13/pflag v1.0.0 // indirect + github.com/spf13/pflag v1.0.2 // indirect github.com/stretchr/testify v1.2.2 - golang.org/x/crypto v0.0.0-20180330210355-12892e8c234f // indirect - golang.org/x/net v0.0.0-20180330215511-b68f30494add // indirect - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect - golang.org/x/sys v0.0.0-20180329131831-378d26f46672 // indirect - golang.org/x/text v0.3.0 // indirect + golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect + golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c // indirect golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 @@ -40,8 +35,12 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.2.1 // indirect - k8s.io/api v0.0.0-20180824172530-dd5c735cbff9 - k8s.io/apimachinery v0.0.0-20180328184639-0ed326127d30 - k8s.io/client-go v8.0.0+incompatible - k8s.io/kube-openapi v0.0.0-20181026222903-0d1aeffe1c68 // indirect + k8s.io/api v0.0.0-20181221193117-173ce66c1e39 + k8s.io/apimachinery v0.0.0-20190119020841-d41becfba9ee + k8s.io/client-go v10.0.0+incompatible + k8s.io/klog v0.2.0 // indirect + k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be // indirect + sigs.k8s.io/yaml v1.1.0 // indirect ) + +replace k8s.io/klog => github.com/mikkeloscar/knolog v0.0.0-20190326191552-80742771eb6b diff --git a/go.sum b/go.sum index def39dd..dc8ccfd 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,40 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aws/aws-sdk-go v1.16.6 h1:pig/KdfESvIv4gUu1B8nVAJAURxbPCTt6e5u79Nqxqc= github.com/aws/aws-sdk-go v1.16.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/evanphx/json-patch v4.1.0+incompatible h1:K1MDoo4AZ4wU0GIU/fPmtZg7VpzLjCxu+UwBD1FvwOc= +github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM= -github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.0.0 h1:lsek0oXi8iFE9L+EXARyHIjU5rlWIhhTkjDz3vHhWWQ= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece h1:3HJXp/18JmMk5sjBP3LDUBtWjczCvynxaeAF6b6kWp8= -github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3 h1:/UewZcckqhvnnS0C6r3Sher2hSEbVmM6Ogpcjen08+Y= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/mikkeloscar/knolog v0.0.0-20190326191552-80742771eb6b h1:5f5B1kp+QerGOF91q1qVJcUWWvXsVEN3OKiyEzAAjIM= +github.com/mikkeloscar/knolog v0.0.0-20190326191552-80742771eb6b/go.mod h1:PizLs/1ddmVrXpFgWOGNmTJ2YHSWUkpUXMYuUkTo3Go= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 h1:ImOHKpmdLPXWX5KSYquUWXKaopEPuY7TPPUo18u9aOI= @@ -48,22 +49,29 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.0.5 h1:8c8b5uO0zS4X6RPl/sd1ENwSkIc0/H2PaHxE3udaE8I= github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/spf13/pflag v1.0.0 h1:oaPbdDe/x0UncahuwiPxW1GYJyilRAdsPnq3e1yaPcI= -github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/crypto v0.0.0-20180330210355-12892e8c234f h1:EDv9U2dOjZ9sVn985FJw58XWqRwhtTnd0RxCfIzqKI8= -golang.org/x/crypto v0.0.0-20180330210355-12892e8c234f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20180330215511-b68f30494add h1:oGr9qHpQTQvl/BmeWw95ZrQKahW4qdIPUiGfQkJYDsA= -golang.org/x/net v0.0.0-20180330215511-b68f30494add/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180329131831-378d26f46672 h1:P+cuqFpXzcxn8UtAwY33SZdbHLKsmjveBNV9V+RoYgQ= -golang.org/x/sys v0.0.0-20180329131831-378d26f46672/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c h1:pcBdqVcrlT+A3i+tWsOROFONQyey9tisIQHI4xqVGLg= +golang.org/x/oauth2 v0.0.0-20190115181402-5dab4167f31c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= @@ -80,11 +88,13 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -k8s.io/api v0.0.0-20180824172530-dd5c735cbff9 h1:WvlsufX0gMkwg1HQSVn6Wr83T7GU2Mucxa0b3MadZfY= -k8s.io/api v0.0.0-20180824172530-dd5c735cbff9/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/apimachinery v0.0.0-20180328184639-0ed326127d30 h1:GgGjfVhZheEBqtbm2zjXlYX8nuT8/nlridBHoJmOguE= -k8s.io/apimachinery v0.0.0-20180328184639-0ed326127d30/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4= -k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/kube-openapi v0.0.0-20181026222903-0d1aeffe1c68 h1:b3RjbG1BRHHU49GfLZ3EQa5Ybt5YtX1clPWTlh1lxUo= -k8s.io/kube-openapi v0.0.0-20181026222903-0d1aeffe1c68/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/api v0.0.0-20181221193117-173ce66c1e39 h1:iGq7zEPXFb0IeXAQK5RiYT1SVKX/af9F9Wv0M+yudPY= +k8s.io/api v0.0.0-20181221193117-173ce66c1e39/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apimachinery v0.0.0-20190119020841-d41becfba9ee h1:3MH/wGFP+9PjyLIMnPN2GYatdJosd+5TnSO2BzQqqo4= +k8s.io/apimachinery v0.0.0-20190119020841-d41becfba9ee/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/client-go v10.0.0+incompatible h1:F1IqCqw7oMBzDkqlcBymRq1450wD0eNqLE9jzUrIi34= +k8s.io/client-go v10.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be h1:aWEq4nbj7HRJ0mtKYjNSk/7X28Tl6TI6FeG8gKF+r7Q= +k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 0000000..4b76f1f --- /dev/null +++ b/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright YEAR 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. +*/ diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh new file mode 100755 index 0000000..5e6cb61 --- /dev/null +++ b/hack/update-codegen.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# Copyright 2019 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. + +set -o errexit +set -o nounset +set -o pipefail + +SRC="github.com" +GOPKG="$SRC/mikkeloscar/kube-aws-iam-controller" +CUSTOM_RESOURCE_NAME="amazonaws.com" +CUSTOM_RESOURCE_VERSION="v1" + +SCRIPT_ROOT="$(dirname "${BASH_SOURCE[0]}")/.." + +# use vendor/ as a temporary stash for code-generator. +rm -rf "${SCRIPT_ROOT}/vendor/k8s.io/code-generator" +rm -rf "${SCRIPT_ROOT}/vendor/k8s.io/gengo" +git clone --branch=release-1.13 https://github.com/kubernetes/code-generator.git "${SCRIPT_ROOT}/vendor/k8s.io/code-generator" +git clone https://github.com/kubernetes/gengo.git "${SCRIPT_ROOT}/vendor/k8s.io/gengo" + +CODEGEN_PKG="${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}" + +# generate the code with: +# --output-base because this script should also be able to run inside the vendor dir of +# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir +# instead of the $GOPATH directly. For normal projects this can be dropped. + +OUTPUT_BASE="$(dirname "${BASH_SOURCE[0]}")/" + +cd "${SCRIPT_ROOT}" +"${CODEGEN_PKG}/generate-groups.sh" all \ + "${GOPKG}/pkg/client" "${GOPKG}/pkg/apis" \ + "${CUSTOM_RESOURCE_NAME}:${CUSTOM_RESOURCE_VERSION}" \ + --go-header-file hack/boilerplate.go.txt \ + --output-base "$OUTPUT_BASE" + +# To use your own boilerplate text append: +# --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt + +# hack to make the generated code work with Go module based projects +cp -r "$OUTPUT_BASE/$GOPKG/pkg/apis" ./pkg +cp -r "$OUTPUT_BASE/$GOPKG/pkg/client" ./pkg +rm -rf "${OUTPUT_BASE:?}${SRC}" diff --git a/main.go b/main.go index 157db59..e3ca9eb 100644 --- a/main.go +++ b/main.go @@ -9,16 +9,17 @@ import ( "time" "github.com/aws/aws-sdk-go/aws/session" + "github.com/mikkeloscar/kube-aws-iam-controller/pkg/clientset" log "github.com/sirupsen/logrus" - "gopkg.in/alecthomas/kingpin.v2" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" + kingpin "gopkg.in/alecthomas/kingpin.v2" + v1 "k8s.io/api/core/v1" ) const ( - defaultInterval = "10s" - defaultRefreshLimit = "15m" - defaultEventQueueSize = "10" + defaultInterval = "10s" + defaultRefreshLimit = "15m" + defaultEventQueueSize = "10" + defaultClientGOTimeout = 30 * time.Second ) var ( @@ -29,6 +30,7 @@ var ( EventQueueSize int BaseRoleARN string APIServer *url.URL + Namespace string } ) @@ -42,6 +44,8 @@ func main() { Default(defaultEventQueueSize).IntVar(&config.EventQueueSize) kingpin.Flag("base-role-arn", "Base Role ARN. If not defined it will be autodiscovered from EC2 Metadata."). StringVar(&config.BaseRoleARN) + kingpin.Flag("namespace", "Limit the controller to a certain namespace."). + Default(v1.NamespaceAll).StringVar(&config.Namespace) kingpin.Flag("apiserver", "API server url.").URLVar(&config.APIServer) kingpin.Parse() @@ -49,18 +53,15 @@ func main() { log.SetLevel(log.DebugLevel) } - var kubeConfig *rest.Config - - if config.APIServer != nil { - kubeConfig = &rest.Config{ - Host: config.APIServer.String(), - } - + ctx, cancel := context.WithCancel(context.Background()) + kubeConfig, err := clientset.ConfigureKubeConfig(config.APIServer, defaultClientGOTimeout, ctx.Done()) + if err != nil { + log.Fatalf("Failed to setup Kubernetes config: %v", err) } - client, err := kubeClient(kubeConfig) + client, err := clientset.NewForConfig(kubeConfig) if err != nil { - log.Fatalf("Failed to setup Kubernetes client: %v", err) + log.Fatalf("Failed to initialize Kubernetes client: %v.", err) } awsSess, err := session.NewSession() @@ -79,27 +80,31 @@ func main() { credsGetter := NewSTSCredentialsGetter(awsSess, config.BaseRoleARN) - stopChs := make([]chan struct{}, 0, 2) - podWatcherStopCh := make(chan struct{}, 1) - stopChs = append(stopChs, podWatcherStopCh) - controllerStopCh := make(chan struct{}, 1) - stopChs = append(stopChs, controllerStopCh) - podsEventCh := make(chan *PodEvent, config.EventQueueSize) controller := NewSecretsController( client, + config.Namespace, config.Interval, config.RefreshLimit, credsGetter, podsEventCh, ) - podWatcher := NewPodWatcher(client, podsEventCh) + podWatcher := NewPodWatcher(client, config.Namespace, podsEventCh) - ctx, cancel := context.WithCancel(context.Background()) go handleSigterm(cancel) + awsIAMRoleController := NewAWSIAMRoleController( + client, + config.Interval, + config.RefreshLimit, + credsGetter, + config.Namespace, + ) + + go awsIAMRoleController.Run(ctx) + podWatcher.Run(ctx) controller.Run(ctx) } @@ -112,20 +117,3 @@ func handleSigterm(cancelFunc func()) { log.Info("Received Term signal. Terminating...") cancelFunc() } - -func kubeClient(config *rest.Config) (kubernetes.Interface, error) { - var err error - if config == nil { - config, err = rest.InClusterConfig() - if err != nil { - return nil, err - } - } - - client, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - - return client, nil -} diff --git a/pkg/apis/amazonaws.com/register.go b/pkg/apis/amazonaws.com/register.go new file mode 100644 index 0000000..43d7c32 --- /dev/null +++ b/pkg/apis/amazonaws.com/register.go @@ -0,0 +1,6 @@ +package amazonaws + +const ( + // GroupName is the group name used in this package. + GroupName = "amazonaws.com" +) diff --git a/pkg/apis/amazonaws.com/v1/register.go b/pkg/apis/amazonaws.com/v1/register.go new file mode 100644 index 0000000..9b21eca --- /dev/null +++ b/pkg/apis/amazonaws.com/v1/register.go @@ -0,0 +1,33 @@ +package v1 + +import ( + amazonaws "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // AddToScheme applies all the stored functions to the scheme. A non-nil error + // indicates that one function failed and the attempt was abandoned. + AddToScheme = schemeBuilder.AddToScheme +) + +// SchemeGroupVersion is the group version used to register these objects. +var SchemeGroupVersion = schema.GroupVersion{Group: amazonaws.GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group-qualified GroupResource. +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// addKnownTypes adds the set of types defined in this package to the supplied scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &AWSIAMRole{}, + &AWSIAMRoleList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/amazonaws.com/v1/types.go b/pkg/apis/amazonaws.com/v1/types.go new file mode 100644 index 0000000..2b0da18 --- /dev/null +++ b/pkg/apis/amazonaws.com/v1/types.go @@ -0,0 +1,50 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// AWSIAMRole describes an AWS IAM Role for which credentials can be +// provisioned in a cluster. +// +k8s:deepcopy-gen=true +type AWSIAMRole struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AWSIAMRoleSpec `json:"spec"` + Status AWSIAMRoleStatus `json:"status"` +} + +// AWSIAMRoleSpec is the spec part of the AWSIAMRole resource. +// +k8s:deepcopy-gen=true +type AWSIAMRoleSpec struct { + RoleReference string `json:"roleReference"` + RoleSessionDuration int64 `json:"roleSessionDuration"` +} + +// AWSIAMRoleStatus is the status section of the AWSIAMRole resource. +// resource. +// +k8s:deepcopy-gen=true +type AWSIAMRoleStatus struct { + // observedGeneration is the most recent generation observed for this + // AWSIAMRole. It corresponds to the AWSIAMRole's generation, which is + // updated on mutation by the API Server. + // +optional + ObservedGeneration *int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"` + RoleARN string `json:"roleARN"` + Expiration *metav1.Time `json:"expiration"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// AWSIAMRoleList is a list of AWSIAMRoles. +// +k8s:deepcopy-gen=true +type AWSIAMRoleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []AWSIAMRole `json:"items"` +} diff --git a/pkg/apis/amazonaws.com/v1/zz_generated.deepcopy.go b/pkg/apis/amazonaws.com/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000..922c92d --- /dev/null +++ b/pkg/apis/amazonaws.com/v1/zz_generated.deepcopy.go @@ -0,0 +1,127 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +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 *AWSIAMRole) DeepCopyInto(out *AWSIAMRole) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSIAMRole. +func (in *AWSIAMRole) DeepCopy() *AWSIAMRole { + if in == nil { + return nil + } + out := new(AWSIAMRole) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AWSIAMRole) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSIAMRoleList) DeepCopyInto(out *AWSIAMRoleList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AWSIAMRole, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSIAMRoleList. +func (in *AWSIAMRoleList) DeepCopy() *AWSIAMRoleList { + if in == nil { + return nil + } + out := new(AWSIAMRoleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AWSIAMRoleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSIAMRoleSpec) DeepCopyInto(out *AWSIAMRoleSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSIAMRoleSpec. +func (in *AWSIAMRoleSpec) DeepCopy() *AWSIAMRoleSpec { + if in == nil { + return nil + } + out := new(AWSIAMRoleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSIAMRoleStatus) DeepCopyInto(out *AWSIAMRoleStatus) { + *out = *in + if in.ObservedGeneration != nil { + in, out := &in.ObservedGeneration, &out.ObservedGeneration + *out = new(int64) + **out = **in + } + if in.Expiration != nil { + in, out := &in.Expiration, &out.Expiration + *out = (*in).DeepCopy() + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSIAMRoleStatus. +func (in *AWSIAMRoleStatus) DeepCopy() *AWSIAMRoleStatus { + if in == nil { + return nil + } + out := new(AWSIAMRoleStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 0000000..9458e20 --- /dev/null +++ b/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,90 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + amazonawsv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/typed/amazonaws.com/v1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + AmazonawsV1() amazonawsv1.AmazonawsV1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + amazonawsV1 *amazonawsv1.AmazonawsV1Client +} + +// AmazonawsV1 retrieves the AmazonawsV1Client +func (c *Clientset) AmazonawsV1() amazonawsv1.AmazonawsV1Interface { + return c.amazonawsV1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.amazonawsV1, err = amazonawsv1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.amazonawsV1 = amazonawsv1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.amazonawsV1 = amazonawsv1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go new file mode 100644 index 0000000..dc992b9 --- /dev/null +++ b/pkg/client/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 0000000..41ed06d --- /dev/null +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,77 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned" + amazonawsv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/typed/amazonaws.com/v1" + fakeamazonawsv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +var _ clientset.Interface = &Clientset{} + +// AmazonawsV1 retrieves the AmazonawsV1Client +func (c *Clientset) AmazonawsV1() amazonawsv1.AmazonawsV1Interface { + return &fakeamazonawsv1.FakeAmazonawsV1{Fake: &c.Fake} +} diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go new file mode 100644 index 0000000..acfa617 --- /dev/null +++ b/pkg/client/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go new file mode 100644 index 0000000..28d8b78 --- /dev/null +++ b/pkg/client/clientset/versioned/fake/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + amazonawsv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + amazonawsv1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000..7f61dc1 --- /dev/null +++ b/pkg/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go new file mode 100644 index 0000000..93ed2f6 --- /dev/null +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + amazonawsv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + amazonawsv1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/amazonaws.com_client.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/amazonaws.com_client.go new file mode 100644 index 0000000..8baa552 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/amazonaws.com_client.go @@ -0,0 +1,90 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type AmazonawsV1Interface interface { + RESTClient() rest.Interface + AWSIAMRolesGetter +} + +// AmazonawsV1Client is used to interact with features provided by the amazonaws.com group. +type AmazonawsV1Client struct { + restClient rest.Interface +} + +func (c *AmazonawsV1Client) AWSIAMRoles(namespace string) AWSIAMRoleInterface { + return newAWSIAMRoles(c, namespace) +} + +// NewForConfig creates a new AmazonawsV1Client for the given config. +func NewForConfig(c *rest.Config) (*AmazonawsV1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AmazonawsV1Client{client}, nil +} + +// NewForConfigOrDie creates a new AmazonawsV1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *AmazonawsV1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new AmazonawsV1Client for the given RESTClient. +func New(c rest.Interface) *AmazonawsV1Client { + return &AmazonawsV1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *AmazonawsV1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/awsiamrole.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/awsiamrole.go new file mode 100644 index 0000000..9487524 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/awsiamrole.go @@ -0,0 +1,191 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + "time" + + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + scheme "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// AWSIAMRolesGetter has a method to return a AWSIAMRoleInterface. +// A group's client should implement this interface. +type AWSIAMRolesGetter interface { + AWSIAMRoles(namespace string) AWSIAMRoleInterface +} + +// AWSIAMRoleInterface has methods to work with AWSIAMRole resources. +type AWSIAMRoleInterface interface { + Create(*v1.AWSIAMRole) (*v1.AWSIAMRole, error) + Update(*v1.AWSIAMRole) (*v1.AWSIAMRole, error) + UpdateStatus(*v1.AWSIAMRole) (*v1.AWSIAMRole, error) + Delete(name string, options *metav1.DeleteOptions) error + DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error + Get(name string, options metav1.GetOptions) (*v1.AWSIAMRole, error) + List(opts metav1.ListOptions) (*v1.AWSIAMRoleList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.AWSIAMRole, err error) + AWSIAMRoleExpansion +} + +// aWSIAMRoles implements AWSIAMRoleInterface +type aWSIAMRoles struct { + client rest.Interface + ns string +} + +// newAWSIAMRoles returns a AWSIAMRoles +func newAWSIAMRoles(c *AmazonawsV1Client, namespace string) *aWSIAMRoles { + return &aWSIAMRoles{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the aWSIAMRole, and returns the corresponding aWSIAMRole object, and an error if there is any. +func (c *aWSIAMRoles) Get(name string, options metav1.GetOptions) (result *v1.AWSIAMRole, err error) { + result = &v1.AWSIAMRole{} + err = c.client.Get(). + Namespace(c.ns). + Resource("awsiamroles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of AWSIAMRoles that match those selectors. +func (c *aWSIAMRoles) List(opts metav1.ListOptions) (result *v1.AWSIAMRoleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.AWSIAMRoleList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("awsiamroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested aWSIAMRoles. +func (c *aWSIAMRoles) Watch(opts metav1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("awsiamroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a aWSIAMRole and creates it. Returns the server's representation of the aWSIAMRole, and an error, if there is any. +func (c *aWSIAMRoles) Create(aWSIAMRole *v1.AWSIAMRole) (result *v1.AWSIAMRole, err error) { + result = &v1.AWSIAMRole{} + err = c.client.Post(). + Namespace(c.ns). + Resource("awsiamroles"). + Body(aWSIAMRole). + Do(). + Into(result) + return +} + +// Update takes the representation of a aWSIAMRole and updates it. Returns the server's representation of the aWSIAMRole, and an error, if there is any. +func (c *aWSIAMRoles) Update(aWSIAMRole *v1.AWSIAMRole) (result *v1.AWSIAMRole, err error) { + result = &v1.AWSIAMRole{} + err = c.client.Put(). + Namespace(c.ns). + Resource("awsiamroles"). + Name(aWSIAMRole.Name). + Body(aWSIAMRole). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *aWSIAMRoles) UpdateStatus(aWSIAMRole *v1.AWSIAMRole) (result *v1.AWSIAMRole, err error) { + result = &v1.AWSIAMRole{} + err = c.client.Put(). + Namespace(c.ns). + Resource("awsiamroles"). + Name(aWSIAMRole.Name). + SubResource("status"). + Body(aWSIAMRole). + Do(). + Into(result) + return +} + +// Delete takes name of the aWSIAMRole and deletes it. Returns an error if one occurs. +func (c *aWSIAMRoles) Delete(name string, options *metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("awsiamroles"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *aWSIAMRoles) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("awsiamroles"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched aWSIAMRole. +func (c *aWSIAMRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.AWSIAMRole, err error) { + result = &v1.AWSIAMRole{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("awsiamroles"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/doc.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/doc.go new file mode 100644 index 0000000..a5e0d4f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1 diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/doc.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/doc.go new file mode 100644 index 0000000..ab4fd43 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/fake_amazonaws.com_client.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/fake_amazonaws.com_client.go new file mode 100644 index 0000000..e057db1 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/fake_amazonaws.com_client.go @@ -0,0 +1,40 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/typed/amazonaws.com/v1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeAmazonawsV1 struct { + *testing.Fake +} + +func (c *FakeAmazonawsV1) AWSIAMRoles(namespace string) v1.AWSIAMRoleInterface { + return &FakeAWSIAMRoles{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeAmazonawsV1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/fake_awsiamrole.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/fake_awsiamrole.go new file mode 100644 index 0000000..1ccf1d6 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/fake/fake_awsiamrole.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + amazonawscomv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeAWSIAMRoles implements AWSIAMRoleInterface +type FakeAWSIAMRoles struct { + Fake *FakeAmazonawsV1 + ns string +} + +var awsiamrolesResource = schema.GroupVersionResource{Group: "amazonaws.com", Version: "v1", Resource: "awsiamroles"} + +var awsiamrolesKind = schema.GroupVersionKind{Group: "amazonaws.com", Version: "v1", Kind: "AWSIAMRole"} + +// Get takes name of the aWSIAMRole, and returns the corresponding aWSIAMRole object, and an error if there is any. +func (c *FakeAWSIAMRoles) Get(name string, options v1.GetOptions) (result *amazonawscomv1.AWSIAMRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(awsiamrolesResource, c.ns, name), &amazonawscomv1.AWSIAMRole{}) + + if obj == nil { + return nil, err + } + return obj.(*amazonawscomv1.AWSIAMRole), err +} + +// List takes label and field selectors, and returns the list of AWSIAMRoles that match those selectors. +func (c *FakeAWSIAMRoles) List(opts v1.ListOptions) (result *amazonawscomv1.AWSIAMRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(awsiamrolesResource, awsiamrolesKind, c.ns, opts), &amazonawscomv1.AWSIAMRoleList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &amazonawscomv1.AWSIAMRoleList{ListMeta: obj.(*amazonawscomv1.AWSIAMRoleList).ListMeta} + for _, item := range obj.(*amazonawscomv1.AWSIAMRoleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested aWSIAMRoles. +func (c *FakeAWSIAMRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(awsiamrolesResource, c.ns, opts)) + +} + +// Create takes the representation of a aWSIAMRole and creates it. Returns the server's representation of the aWSIAMRole, and an error, if there is any. +func (c *FakeAWSIAMRoles) Create(aWSIAMRole *amazonawscomv1.AWSIAMRole) (result *amazonawscomv1.AWSIAMRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(awsiamrolesResource, c.ns, aWSIAMRole), &amazonawscomv1.AWSIAMRole{}) + + if obj == nil { + return nil, err + } + return obj.(*amazonawscomv1.AWSIAMRole), err +} + +// Update takes the representation of a aWSIAMRole and updates it. Returns the server's representation of the aWSIAMRole, and an error, if there is any. +func (c *FakeAWSIAMRoles) Update(aWSIAMRole *amazonawscomv1.AWSIAMRole) (result *amazonawscomv1.AWSIAMRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(awsiamrolesResource, c.ns, aWSIAMRole), &amazonawscomv1.AWSIAMRole{}) + + if obj == nil { + return nil, err + } + return obj.(*amazonawscomv1.AWSIAMRole), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeAWSIAMRoles) UpdateStatus(aWSIAMRole *amazonawscomv1.AWSIAMRole) (*amazonawscomv1.AWSIAMRole, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(awsiamrolesResource, "status", c.ns, aWSIAMRole), &amazonawscomv1.AWSIAMRole{}) + + if obj == nil { + return nil, err + } + return obj.(*amazonawscomv1.AWSIAMRole), err +} + +// Delete takes name of the aWSIAMRole and deletes it. Returns an error if one occurs. +func (c *FakeAWSIAMRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(awsiamrolesResource, c.ns, name), &amazonawscomv1.AWSIAMRole{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeAWSIAMRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(awsiamrolesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &amazonawscomv1.AWSIAMRoleList{}) + return err +} + +// Patch applies the patch and returns the patched aWSIAMRole. +func (c *FakeAWSIAMRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *amazonawscomv1.AWSIAMRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(awsiamrolesResource, c.ns, name, pt, data, subresources...), &amazonawscomv1.AWSIAMRole{}) + + if obj == nil { + return nil, err + } + return obj.(*amazonawscomv1.AWSIAMRole), err +} diff --git a/pkg/client/clientset/versioned/typed/amazonaws.com/v1/generated_expansion.go b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/generated_expansion.go new file mode 100644 index 0000000..cd5f954 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/amazonaws.com/v1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +type AWSIAMRoleExpansion interface{} diff --git a/pkg/client/informers/externalversions/amazonaws.com/interface.go b/pkg/client/informers/externalversions/amazonaws.com/interface.go new file mode 100644 index 0000000..35198d7 --- /dev/null +++ b/pkg/client/informers/externalversions/amazonaws.com/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package amazonaws + +import ( + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/informers/externalversions/amazonaws.com/v1" + internalinterfaces "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1 provides access to shared informers for resources in V1. + V1() v1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1 returns a new v1.Interface. +func (g *group) V1() v1.Interface { + return v1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/amazonaws.com/v1/awsiamrole.go b/pkg/client/informers/externalversions/amazonaws.com/v1/awsiamrole.go new file mode 100644 index 0000000..e6789bd --- /dev/null +++ b/pkg/client/informers/externalversions/amazonaws.com/v1/awsiamrole.go @@ -0,0 +1,89 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + time "time" + + amazonawscomv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + versioned "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned" + internalinterfaces "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/informers/externalversions/internalinterfaces" + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/listers/amazonaws.com/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// AWSIAMRoleInformer provides access to a shared informer and lister for +// AWSIAMRoles. +type AWSIAMRoleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.AWSIAMRoleLister +} + +type aWSIAMRoleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewAWSIAMRoleInformer constructs a new informer for AWSIAMRole type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewAWSIAMRoleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredAWSIAMRoleInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredAWSIAMRoleInformer constructs a new informer for AWSIAMRole type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredAWSIAMRoleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AmazonawsV1().AWSIAMRoles(namespace).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AmazonawsV1().AWSIAMRoles(namespace).Watch(options) + }, + }, + &amazonawscomv1.AWSIAMRole{}, + resyncPeriod, + indexers, + ) +} + +func (f *aWSIAMRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredAWSIAMRoleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *aWSIAMRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&amazonawscomv1.AWSIAMRole{}, f.defaultInformer) +} + +func (f *aWSIAMRoleInformer) Lister() v1.AWSIAMRoleLister { + return v1.NewAWSIAMRoleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/amazonaws.com/v1/interface.go b/pkg/client/informers/externalversions/amazonaws.com/v1/interface.go new file mode 100644 index 0000000..6adcc6c --- /dev/null +++ b/pkg/client/informers/externalversions/amazonaws.com/v1/interface.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + internalinterfaces "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // AWSIAMRoles returns a AWSIAMRoleInformer. + AWSIAMRoles() AWSIAMRoleInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// AWSIAMRoles returns a AWSIAMRoleInformer. +func (v *version) AWSIAMRoles() AWSIAMRoleInformer { + return &aWSIAMRoleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go new file mode 100644 index 0000000..bed6a8b --- /dev/null +++ b/pkg/client/informers/externalversions/factory.go @@ -0,0 +1,180 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned" + amazonawscom "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/informers/externalversions/amazonaws.com" + internalinterfaces "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/informers/externalversions/internalinterfaces" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Amazonaws() amazonawscom.Interface +} + +func (f *sharedInformerFactory) Amazonaws() amazonawscom.Interface { + return amazonawscom.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go new file mode 100644 index 0000000..49dff94 --- /dev/null +++ b/pkg/client/informers/externalversions/generic.go @@ -0,0 +1,62 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=amazonaws.com, Version=v1 + case v1.SchemeGroupVersion.WithResource("awsiamroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Amazonaws().V1().AWSIAMRoles().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000..6feff6f --- /dev/null +++ b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,40 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/client/listers/amazonaws.com/v1/awsiamrole.go b/pkg/client/listers/amazonaws.com/v1/awsiamrole.go new file mode 100644 index 0000000..b75c19b --- /dev/null +++ b/pkg/client/listers/amazonaws.com/v1/awsiamrole.go @@ -0,0 +1,94 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/apis/amazonaws.com/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// AWSIAMRoleLister helps list AWSIAMRoles. +type AWSIAMRoleLister interface { + // List lists all AWSIAMRoles in the indexer. + List(selector labels.Selector) (ret []*v1.AWSIAMRole, err error) + // AWSIAMRoles returns an object that can list and get AWSIAMRoles. + AWSIAMRoles(namespace string) AWSIAMRoleNamespaceLister + AWSIAMRoleListerExpansion +} + +// aWSIAMRoleLister implements the AWSIAMRoleLister interface. +type aWSIAMRoleLister struct { + indexer cache.Indexer +} + +// NewAWSIAMRoleLister returns a new AWSIAMRoleLister. +func NewAWSIAMRoleLister(indexer cache.Indexer) AWSIAMRoleLister { + return &aWSIAMRoleLister{indexer: indexer} +} + +// List lists all AWSIAMRoles in the indexer. +func (s *aWSIAMRoleLister) List(selector labels.Selector) (ret []*v1.AWSIAMRole, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.AWSIAMRole)) + }) + return ret, err +} + +// AWSIAMRoles returns an object that can list and get AWSIAMRoles. +func (s *aWSIAMRoleLister) AWSIAMRoles(namespace string) AWSIAMRoleNamespaceLister { + return aWSIAMRoleNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// AWSIAMRoleNamespaceLister helps list and get AWSIAMRoles. +type AWSIAMRoleNamespaceLister interface { + // List lists all AWSIAMRoles in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1.AWSIAMRole, err error) + // Get retrieves the AWSIAMRole from the indexer for a given namespace and name. + Get(name string) (*v1.AWSIAMRole, error) + AWSIAMRoleNamespaceListerExpansion +} + +// aWSIAMRoleNamespaceLister implements the AWSIAMRoleNamespaceLister +// interface. +type aWSIAMRoleNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all AWSIAMRoles in the indexer for a given namespace. +func (s aWSIAMRoleNamespaceLister) List(selector labels.Selector) (ret []*v1.AWSIAMRole, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.AWSIAMRole)) + }) + return ret, err +} + +// Get retrieves the AWSIAMRole from the indexer for a given namespace and name. +func (s aWSIAMRoleNamespaceLister) Get(name string) (*v1.AWSIAMRole, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("awsiamrole"), name) + } + return obj.(*v1.AWSIAMRole), nil +} diff --git a/pkg/client/listers/amazonaws.com/v1/expansion_generated.go b/pkg/client/listers/amazonaws.com/v1/expansion_generated.go new file mode 100644 index 0000000..07cb5ed --- /dev/null +++ b/pkg/client/listers/amazonaws.com/v1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright 2019 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +// AWSIAMRoleListerExpansion allows custom methods to be added to +// AWSIAMRoleLister. +type AWSIAMRoleListerExpansion interface{} + +// AWSIAMRoleNamespaceListerExpansion allows custom methods to be added to +// AWSIAMRoleNamespaceLister. +type AWSIAMRoleNamespaceListerExpansion interface{} diff --git a/pkg/clientset/unified.go b/pkg/clientset/unified.go new file mode 100644 index 0000000..1a2ede6 --- /dev/null +++ b/pkg/clientset/unified.go @@ -0,0 +1,214 @@ +package clientset + +import ( + "net" + "net/http" + "net/url" + "time" + + awsiamrole "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned" + amazonawsv1 "github.com/mikkeloscar/kube-aws-iam-controller/pkg/client/clientset/versioned/typed/amazonaws.com/v1" + discovery "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" + admissionregistrationv1alpha1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1" + admissionregistrationv1beta1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" + appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" + appsv1beta1 "k8s.io/client-go/kubernetes/typed/apps/v1beta1" + appsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2" + auditregistrationv1alpha1 "k8s.io/client-go/kubernetes/typed/auditregistration/v1alpha1" + authenticationv1 "k8s.io/client-go/kubernetes/typed/authentication/v1" + authenticationv1beta1 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1" + authorizationv1 "k8s.io/client-go/kubernetes/typed/authorization/v1" + authorizationv1beta1 "k8s.io/client-go/kubernetes/typed/authorization/v1beta1" + autoscalingv1 "k8s.io/client-go/kubernetes/typed/autoscaling/v1" + autoscalingv2beta1 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1" + autoscalingv2beta2 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2" + batchv1 "k8s.io/client-go/kubernetes/typed/batch/v1" + batchv1beta1 "k8s.io/client-go/kubernetes/typed/batch/v1beta1" + batchv2alpha1 "k8s.io/client-go/kubernetes/typed/batch/v2alpha1" + certificatesv1beta1 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" + coordinationv1beta1 "k8s.io/client-go/kubernetes/typed/coordination/v1beta1" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + eventsv1beta1 "k8s.io/client-go/kubernetes/typed/events/v1beta1" + extensionsv1beta1 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" + networkingv1 "k8s.io/client-go/kubernetes/typed/networking/v1" + policyv1beta1 "k8s.io/client-go/kubernetes/typed/policy/v1beta1" + rbacv1 "k8s.io/client-go/kubernetes/typed/rbac/v1" + rbacv1alpha1 "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1" + rbacv1beta1 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1" + schedulingv1alpha1 "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1" + schedulingv1beta1 "k8s.io/client-go/kubernetes/typed/scheduling/v1beta1" + settingsv1alpha1 "k8s.io/client-go/kubernetes/typed/settings/v1alpha1" + storagev1 "k8s.io/client-go/kubernetes/typed/storage/v1" + storagev1alpha1 "k8s.io/client-go/kubernetes/typed/storage/v1alpha1" + storagev1beta1 "k8s.io/client-go/kubernetes/typed/storage/v1beta1" + rest "k8s.io/client-go/rest" + "k8s.io/client-go/transport" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + AdmissionregistrationV1alpha1() admissionregistrationv1alpha1.AdmissionregistrationV1alpha1Interface + AdmissionregistrationV1beta1() admissionregistrationv1beta1.AdmissionregistrationV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Admissionregistration() admissionregistrationv1beta1.AdmissionregistrationV1beta1Interface + AppsV1beta1() appsv1beta1.AppsV1beta1Interface + AppsV1beta2() appsv1beta2.AppsV1beta2Interface + AppsV1() appsv1.AppsV1Interface + // Deprecated: please explicitly pick a version if possible. + Apps() appsv1.AppsV1Interface + AuditregistrationV1alpha1() auditregistrationv1alpha1.AuditregistrationV1alpha1Interface + // Deprecated: please explicitly pick a version if possible. + Auditregistration() auditregistrationv1alpha1.AuditregistrationV1alpha1Interface + AuthenticationV1() authenticationv1.AuthenticationV1Interface + // Deprecated: please explicitly pick a version if possible. + Authentication() authenticationv1.AuthenticationV1Interface + AuthenticationV1beta1() authenticationv1beta1.AuthenticationV1beta1Interface + AuthorizationV1() authorizationv1.AuthorizationV1Interface + // Deprecated: please explicitly pick a version if possible. + Authorization() authorizationv1.AuthorizationV1Interface + AuthorizationV1beta1() authorizationv1beta1.AuthorizationV1beta1Interface + AutoscalingV1() autoscalingv1.AutoscalingV1Interface + // Deprecated: please explicitly pick a version if possible. + Autoscaling() autoscalingv1.AutoscalingV1Interface + AutoscalingV2beta1() autoscalingv2beta1.AutoscalingV2beta1Interface + AutoscalingV2beta2() autoscalingv2beta2.AutoscalingV2beta2Interface + BatchV1() batchv1.BatchV1Interface + // Deprecated: please explicitly pick a version if possible. + Batch() batchv1.BatchV1Interface + BatchV1beta1() batchv1beta1.BatchV1beta1Interface + BatchV2alpha1() batchv2alpha1.BatchV2alpha1Interface + CertificatesV1beta1() certificatesv1beta1.CertificatesV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Certificates() certificatesv1beta1.CertificatesV1beta1Interface + CoordinationV1beta1() coordinationv1beta1.CoordinationV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Coordination() coordinationv1beta1.CoordinationV1beta1Interface + CoreV1() corev1.CoreV1Interface + // Deprecated: please explicitly pick a version if possible. + Core() corev1.CoreV1Interface + EventsV1beta1() eventsv1beta1.EventsV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Events() eventsv1beta1.EventsV1beta1Interface + ExtensionsV1beta1() extensionsv1beta1.ExtensionsV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Extensions() extensionsv1beta1.ExtensionsV1beta1Interface + NetworkingV1() networkingv1.NetworkingV1Interface + // Deprecated: please explicitly pick a version if possible. + Networking() networkingv1.NetworkingV1Interface + PolicyV1beta1() policyv1beta1.PolicyV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Policy() policyv1beta1.PolicyV1beta1Interface + RbacV1() rbacv1.RbacV1Interface + // Deprecated: please explicitly pick a version if possible. + Rbac() rbacv1.RbacV1Interface + RbacV1beta1() rbacv1beta1.RbacV1beta1Interface + RbacV1alpha1() rbacv1alpha1.RbacV1alpha1Interface + SchedulingV1alpha1() schedulingv1alpha1.SchedulingV1alpha1Interface + SchedulingV1beta1() schedulingv1beta1.SchedulingV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Scheduling() schedulingv1beta1.SchedulingV1beta1Interface + SettingsV1alpha1() settingsv1alpha1.SettingsV1alpha1Interface + // Deprecated: please explicitly pick a version if possible. + Settings() settingsv1alpha1.SettingsV1alpha1Interface + StorageV1beta1() storagev1beta1.StorageV1beta1Interface + StorageV1() storagev1.StorageV1Interface + // Deprecated: please explicitly pick a version if possible. + Storage() storagev1.StorageV1Interface + StorageV1alpha1() storagev1alpha1.StorageV1alpha1Interface + AmazonawsV1() amazonawsv1.AmazonawsV1Interface +} + +type Clientset struct { + kubernetes.Interface + awsiamrole awsiamrole.Interface +} + +func NewClientset(kubernetes kubernetes.Interface, awsiamrole awsiamrole.Interface) *Clientset { + return &Clientset{ + kubernetes, + awsiamrole, + } +} + +func NewForConfig(kubeconfig *rest.Config) (*Clientset, error) { + kubeClient, err := kubernetes.NewForConfig(kubeconfig) + if err != nil { + return nil, err + } + + awsIAMRoleClient, err := awsiamrole.NewForConfig(kubeconfig) + if err != nil { + return nil, err + } + + return &Clientset{kubeClient, awsIAMRoleClient}, nil +} + +func (c *Clientset) AmazonawsV1() amazonawsv1.AmazonawsV1Interface { + return c.awsiamrole.AmazonawsV1() +} + +// ConfigureKubeConfig configures a kubeconfig. +func ConfigureKubeConfig(apiServerURL *url.URL, timeout time.Duration, stopCh <-chan struct{}) (*rest.Config, error) { + tr := &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: timeout, + KeepAlive: 30 * time.Second, + DualStack: false, // K8s do not work well with IPv6 + }).DialContext, + TLSHandshakeTimeout: timeout, + ResponseHeaderTimeout: 10 * time.Second, + MaxIdleConns: 10, + MaxIdleConnsPerHost: 2, + IdleConnTimeout: 20 * time.Second, + } + + // We need this to reliably fade on DNS change, which is right + // now not fixed with IdleConnTimeout in the http.Transport. + // https://github.com/golang/go/issues/23427 + go func(d time.Duration) { + for { + select { + case <-time.After(d): + tr.CloseIdleConnections() + case <-stopCh: + return + } + } + }(20 * time.Second) + + if apiServerURL != nil { + return &rest.Config{ + Host: apiServerURL.String(), + Timeout: timeout, + Transport: tr, + QPS: 100.0, + Burst: 500, + }, nil + } + + config, err := rest.InClusterConfig() + if err != nil { + return nil, err + } + + // patch TLS config + restTransportConfig, err := config.TransportConfig() + if err != nil { + return nil, err + } + restTLSConfig, err := transport.TLSConfigFor(restTransportConfig) + if err != nil { + return nil, err + } + tr.TLSClientConfig = restTLSConfig + + config.Timeout = timeout + config.Transport = tr + config.QPS = 100.0 + config.Burst = 500 + // disable TLSClientConfig to make the custom Transport work + config.TLSClientConfig = rest.TLSClientConfig{} + return config, nil +} diff --git a/pkg/recorder/recorder.go b/pkg/recorder/recorder.go new file mode 100644 index 0000000..e7df369 --- /dev/null +++ b/pkg/recorder/recorder.go @@ -0,0 +1,22 @@ +package recorder + +import ( + clientv1 "k8s.io/api/core/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/kubernetes/scheme" + v1core "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/record" + + "github.com/sirupsen/logrus" +) + +// CreateEventRecorder creates an event recorder to send custom events to Kubernetes to be recorded for targeted Kubernetes objects +func CreateEventRecorder(kubeClient clientset.Interface) record.EventRecorder { + eventBroadcaster := record.NewBroadcaster() + eventBroadcaster.StartLogging(logrus.Infof) + if _, isfake := kubeClient.(*fake.Clientset); !isfake { + eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubeClient.CoreV1().RESTClient()).Events("")}) + } + return eventBroadcaster.NewRecorder(scheme.Scheme, clientv1.EventSource{Component: "kube-aws-iam-controller"}) +} diff --git a/pod_watcher.go b/pod_watcher.go index 0907b3b..c8113de 100644 --- a/pod_watcher.go +++ b/pod_watcher.go @@ -23,12 +23,14 @@ type PodEvent struct { type PodWatcher struct { client kubernetes.Interface podEvents chan<- *PodEvent + namespace string } // NewPodWatcher initializes a new PodWatcher. -func NewPodWatcher(client kubernetes.Interface, podEvents chan<- *PodEvent) *PodWatcher { +func NewPodWatcher(client kubernetes.Interface, namespace string, podEvents chan<- *PodEvent) *PodWatcher { return &PodWatcher{ client: client, + namespace: namespace, podEvents: podEvents, } } @@ -37,7 +39,7 @@ func NewPodWatcher(client kubernetes.Interface, podEvents chan<- *PodEvent) *Pod // starts listening for events. func (p *PodWatcher) Run(ctx context.Context) { informer := cache.NewSharedIndexInformer( - cache.NewListWatchFromClient(p.client.CoreV1().RESTClient(), "pods", v1.NamespaceAll, fields.Everything()), + cache.NewListWatchFromClient(p.client.CoreV1().RESTClient(), "pods", p.namespace, fields.Everything()), &v1.Pod{}, 0, // skip resync cache.Indexers{}, diff --git a/pod_watcher_test.go b/pod_watcher_test.go index 0da5ce4..ff3b738 100644 --- a/pod_watcher_test.go +++ b/pod_watcher_test.go @@ -23,7 +23,7 @@ func TestPodWatcherAdd(t *testing.T) { } events := make(chan *PodEvent, 1) - watcher := NewPodWatcher(nil, events) + watcher := NewPodWatcher(nil, v1.NamespaceAll, events) go watcher.add(pod) <-events @@ -48,7 +48,7 @@ func TestPodWatcherDel(t *testing.T) { } events := make(chan *PodEvent, 1) - watcher := NewPodWatcher(nil, events) + watcher := NewPodWatcher(nil, v1.NamespaceAll, events) go watcher.del(pod) <-events diff --git a/secrets_controller.go b/secrets_controller.go index 149fcdb..b02e60c 100644 --- a/secrets_controller.go +++ b/secrets_controller.go @@ -18,6 +18,7 @@ const ( heritageLabelKey = "heritage" awsIAMControllerLabelValue = "kube-aws-iam-controller" secretPrefix = "aws-iam-" + roleARNKey = "role-arn" expireKey = "expire" credentialsFileKey = "credentials" credentialsFileTemplate = `[default] @@ -46,20 +47,21 @@ type SecretsController struct { creds CredentialsGetter roleStore *RoleStore podEvents <-chan *PodEvent + namespace string } // ProcessCredentials defines the format expected from process credentials. // https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes type ProcessCredentials struct { - Version int `json:"Version"` - AccessKeyID string `json:"AccessKeyId"` - SecretAccessKey string `json:"SecretAccessKey"` - SessionToken string `json:"SessionToken"` - Expiration *time.Time `json:"Expiration"` + Version int `json:"Version"` + AccessKeyID string `json:"AccessKeyId"` + SecretAccessKey string `json:"SecretAccessKey"` + SessionToken string `json:"SessionToken"` + Expiration time.Time `json:"Expiration"` } // NewSecretsController initializes a new SecretsController. -func NewSecretsController(client kubernetes.Interface, interval, refreshLimit time.Duration, creds CredentialsGetter, podEvents <-chan *PodEvent) *SecretsController { +func NewSecretsController(client kubernetes.Interface, namespace string, interval, refreshLimit time.Duration, creds CredentialsGetter, podEvents <-chan *PodEvent) *SecretsController { return &SecretsController{ client: client, interval: interval, @@ -67,13 +69,14 @@ func NewSecretsController(client kubernetes.Interface, interval, refreshLimit ti creds: creds, roleStore: NewRoleStore(), podEvents: podEvents, + namespace: namespace, } } // getCreds gets new credentials from the CredentialsGetter and converts them // to a secret data map. func (c *SecretsController) getCreds(role string) (map[string][]byte, error) { - creds, err := c.creds.Get(role) + creds, err := c.creds.Get(role, 3600*time.Second) if err != nil { return nil, err } @@ -152,7 +155,7 @@ func (c *SecretsController) refresh() error { LabelSelector: labels.Set(ownerLabels).AsSelector().String(), } - secrets, err := c.client.CoreV1().Secrets(v1.NamespaceAll).List(opts) + secrets, err := c.client.CoreV1().Secrets(c.namespace).List(opts) if err != nil { return err } @@ -162,6 +165,11 @@ func (c *SecretsController) refresh() error { tmpSecretStore := NewRoleStore() for _, secret := range secrets.Items { + // skip secrets owned by someone + if len(secret.OwnerReferences) > 0 { + continue + } + role := strings.TrimPrefix(secret.Name, secretPrefix) // store found secrets in a tmp store so we can lookup missing diff --git a/secrets_controller_test.go b/secrets_controller_test.go index 19c7823..15fc254 100644 --- a/secrets_controller_test.go +++ b/secrets_controller_test.go @@ -15,7 +15,7 @@ type mockCredsGetter struct { creds *Credentials } -func (g *mockCredsGetter) Get(role string) (*Credentials, error) { +func (g *mockCredsGetter) Get(role string, sessionDuration time.Duration) (*Credentials, error) { if g.err != nil { return nil, g.err } @@ -151,6 +151,7 @@ func TestRefresh(tt *testing.T) { tt.Run(ti.msg, func(t *testing.T) { controller := NewSecretsController( fake.NewSimpleClientset(), + v1.NamespaceAll, time.Second, time.Second, &mockCredsGetter{ @@ -158,7 +159,7 @@ func TestRefresh(tt *testing.T) { AccessKeyID: "access_key_id", SecretAccessKey: "secret_access_key", SessionToken: "session_token", - Expiration: &timeFuture, + Expiration: timeFuture, }, }, make(chan *PodEvent, 1),