Skip to content

Commit

Permalink
feat: robust and improved ConfigMap management.
Browse files Browse the repository at this point in the history
- BREAKING CHANGE: Renamed CRD and added Status.
- Update the ConfigMap while preserving existing entries.
- Use a finalizer to clean up entries in the ConfigMap.
- Added some tests.
  • Loading branch information
henninge committed Jan 4, 2022
1 parent 160168a commit 95a99a7
Show file tree
Hide file tree
Showing 20 changed files with 733 additions and 339 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Test

on:
push:
branches:
- '*'
pull_request: {}

env:
GO_VERSION: '1.17'
KUBERNETES_VERSION: '1.19.x'

jobs:
run-tests:
runs-on: ubuntu-18.04

steps:
- name: checkout
uses: actions/checkout@v2

- name: install go
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}

- name: run tests
run: |
go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
eval `setup-envtest use ${{ env.KUBERNETES_VERSION }} -p env`
make test
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,8 @@ fmt: ## Run go fmt against code.
vet: ## Run go vet against code.
go vet ./...

ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
test: manifests generate fmt vet ## Run tests.
mkdir -p ${ENVTEST_ASSETS_DIR}
test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.3/hack/setup-envtest.sh
source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
go test ./... -coverprofile cover.out

##@ Build

Expand Down
3 changes: 1 addition & 2 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ repo: github.com/inovex/aws-auth-controller
resources:
- api:
crdVersion: v1
namespaced: false
controller: true
domain: awsauth.io
group: crd
kind: AwsAuthMap
kind: AwsAuthMapSnippet
path: github.com/inovex/aws-auth-controller/api/v1beta1
version: v1beta1
version: "3"
23 changes: 4 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ To pick up on the example from the AWS documentation, that snippet would
look like this in the CR:

apiVersion: crd.awsauth.io/v1beta1
kind: AwsAuthMap
kind: AwsAuthMapSnippet
metadata:
name: awsauthmap-sample
name: awsauthmapsnippet-sample
namespace: sample-namespace
spec:
mapRoles:
Expand All @@ -57,17 +57,6 @@ When this resource is added to the cluster, the controller will modify the
configmap to include the entries in this snippet. When it is removed, the
respective entries will be removed, too.

## Implementation details

The controller adds an annotation to the configmap `awsauth.io/authversion`
that holds an incrementing serial number to match the configmap to the
custom resources in the cluster. The same value is stored in the CR's
status so that out-of-sync resources can be identified.

The controller calculates the SHA256 checksum of the snippet's content
(arns, usernames, groupnames) to detect changed snippets that need to be
updated in the configmap. The checksum is also stored in the status.

## Controller deployment

A working single-file deployment manifest is forthcoming. For now the
Expand All @@ -93,10 +82,6 @@ It will use whatever kubectl context is currently active.
## TODOs

* Semantic release versioning
* Clean up repository
* A validating webhook to
* check the ARN format
* check for duplicate ARNs
* check with AWS if ARN actually exists (?, needs IRSA)
* A validating webhook to check with AWS if ARN actually exists (?, needs IRSA)
* Add `mapAccounts` if anybody needs it
* Tests, automated
* More tests
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ limitations under the License.
package v1beta1

import (
"crypto/sha256"
"fmt"
"io"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -43,68 +39,43 @@ type MapUsersSpec struct {
Groups []string `json:"groups"`
}

// AwsAuthMapSpec defines the IAM role and user mappings to RBAC.
type AwsAuthMapSpec struct {
// AwsAuthMapSnippetSpec defines the IAM role and user mappings to RBAC.
type AwsAuthMapSnippetSpec struct {
MapRoles []MapRolesSpec `json:"mapRoles,omitempty"`
MapUsers []MapUsersSpec `json:"mapUsers,omitempty"`
}

// CalcCheckSum adds all string values into a checksum for identifying changes.
func (s *AwsAuthMapSpec) CalcCheckSum() string {
shaHash := sha256.New()
for _, mr := range s.MapRoles {
io.WriteString(shaHash, mr.RoleArn)
io.WriteString(shaHash, mr.UserName)
for _, group := range mr.Groups {
io.WriteString(shaHash, group)
}
}
for _, mu := range s.MapUsers {
io.WriteString(shaHash, mu.UserArn)
io.WriteString(shaHash, mu.UserName)
for _, group := range mu.Groups {
io.WriteString(shaHash, group)
}
}
return fmt.Sprintf("%x", shaHash.Sum(nil))
}

// AwsAuthMapStatus defines the observed state of AwsAuthMap.
type AwsAuthMapStatus struct {
// AwsAuthMapSnippetStatus defines the observed state of AwsAuthMapSnippet.
type AwsAuthMapSnippetStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file

MapVersion int `json:"mapVersion"`
CheckSum string `json:"checkSum"`
RoleArns []string `json:"roleArns,omitempty"`
UserArns []string `json:"userArns,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="MapVersion",type=integer,JSONPath=`.status.mapVersion`

// AwsAuthMap is the Schema for the awsauthmaps API
type AwsAuthMap struct {
// AwsAuthMapSnippet is the Schema for the awsauthmapsnippets API
type AwsAuthMapSnippet struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec AwsAuthMapSpec `json:"spec,omitempty"`
Status AwsAuthMapStatus `json:"status,omitempty"`
}

// IsChanged indicates if the stored and calculated checksums differ.
func (m *AwsAuthMap) IsChanged() bool {
return m.Spec.CalcCheckSum() != m.Status.CheckSum
Spec AwsAuthMapSnippetSpec `json:"spec,omitempty"`
Status AwsAuthMapSnippetStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// AwsAuthMapList contains a list of AwsAuthMap
type AwsAuthMapList struct {
// AwsAuthMapSnippetList contains a list of AwsAuthMapSnippet
type AwsAuthMapSnippetList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AwsAuthMap `json:"items"`
Items []AwsAuthMapSnippet `json:"items"`
}

func init() {
SchemeBuilder.Register(&AwsAuthMap{}, &AwsAuthMapList{})
SchemeBuilder.Register(&AwsAuthMapSnippet{}, &AwsAuthMapSnippetList{})
}
50 changes: 30 additions & 20 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 95a99a7

Please sign in to comment.