From 1e5db53740d0e83d484886d228608cbad1962ec4 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Tue, 21 Feb 2023 00:25:56 +0000
Subject: [PATCH 01/16] Adding Credential Package with source and helm chart

---
 credentialproviderpackage/Dockerfile          |  14 +
 credentialproviderpackage/Makefile            |  41 +++
 .../credential-provider-package/.helmignore   |  23 ++
 .../credential-provider-package/Chart.yaml    |  24 ++
 .../templates/NOTES.txt                       |   1 +
 .../templates/_helpers.tpl                    |  88 +++++
 .../templates/deployment.yaml                 |  89 +++++
 .../templates/serviceaccount.yaml             |  12 +
 .../credential-provider-package/values.yaml   |  62 ++++
 .../cmd/aws-credential-provider/main.go       | 119 +++++++
 credentialproviderpackage/go.mod              |  28 ++
 credentialproviderpackage/go.sum              |  87 +++++
 .../internal/test/files.go                    | 137 ++++++++
 .../configurator/bottlerocket/bottlerocket.go | 171 ++++++++++
 .../bottlerocket/bottlerocket_test.go         | 323 ++++++++++++++++++
 .../bottlerocket/testdata/testcreds           |   3 +
 .../pkg/configurator/configurator.go          |  17 +
 .../pkg/configurator/linux/linux.go           | 223 ++++++++++++
 .../pkg/configurator/linux/linux_test.go      | 212 ++++++++++++
 .../templates/credential-provider-config.yaml |  17 +
 .../linux/testdata/expected-config.yaml       |  17 +
 .../pkg/configurator/linux/testdata/testcreds |   3 +
 .../pkg/constants/constants.go                |  41 +++
 .../pkg/filewriter/filewriter.go              |  23 ++
 .../pkg/filewriter/filewriter_defaults.go     |  19 ++
 .../pkg/filewriter/tmp_writer_test.go         | 159 +++++++++
 .../pkg/filewriter/writer.go                  |  99 ++++++
 .../pkg/filewriter/writer_test.go             | 203 +++++++++++
 .../pkg/templater/partialyaml.go              |  31 ++
 .../pkg/templater/partialyaml_test.go         | 131 +++++++
 .../pkg/templater/templater.go                |  66 ++++
 .../pkg/templater/templater_test.go           | 143 ++++++++
 .../templater/testdata/invalid_template.yaml  |   5 +
 .../pkg/templater/testdata/key4_template.yaml |   6 +
 .../testdata/partial_yaml_array_expected.yaml |   8 +
 .../testdata/partial_yaml_map_expected.yaml   |   8 +
 .../partial_yaml_object_expected.yaml         |   3 +
 .../test1_conditional_false_want.yaml         |   2 +
 .../testdata/test1_conditional_true_want.yaml |   4 +
 .../templater/testdata/test1_template.yaml    |   5 +
 .../testdata/test_indent_template.yaml        |   5 +
 .../templater/testdata/test_indent_want.yaml  |   4 +
 .../pkg/templater/yaml.go                     |  39 +++
 credentialproviderpackage/pkg/utils/utils.go  |  18 +
 credentialproviderpackage/skaffold.yaml       |  24 ++
 45 files changed, 2757 insertions(+)
 create mode 100644 credentialproviderpackage/Dockerfile
 create mode 100644 credentialproviderpackage/Makefile
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/.helmignore
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/Chart.yaml
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml
 create mode 100644 credentialproviderpackage/charts/credential-provider-package/values.yaml
 create mode 100644 credentialproviderpackage/cmd/aws-credential-provider/main.go
 create mode 100644 credentialproviderpackage/go.mod
 create mode 100644 credentialproviderpackage/go.sum
 create mode 100644 credentialproviderpackage/internal/test/files.go
 create mode 100644 credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
 create mode 100644 credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
 create mode 100644 credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds
 create mode 100644 credentialproviderpackage/pkg/configurator/configurator.go
 create mode 100644 credentialproviderpackage/pkg/configurator/linux/linux.go
 create mode 100644 credentialproviderpackage/pkg/configurator/linux/linux_test.go
 create mode 100644 credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml
 create mode 100644 credentialproviderpackage/pkg/configurator/linux/testdata/expected-config.yaml
 create mode 100644 credentialproviderpackage/pkg/configurator/linux/testdata/testcreds
 create mode 100644 credentialproviderpackage/pkg/constants/constants.go
 create mode 100644 credentialproviderpackage/pkg/filewriter/filewriter.go
 create mode 100644 credentialproviderpackage/pkg/filewriter/filewriter_defaults.go
 create mode 100644 credentialproviderpackage/pkg/filewriter/tmp_writer_test.go
 create mode 100644 credentialproviderpackage/pkg/filewriter/writer.go
 create mode 100644 credentialproviderpackage/pkg/filewriter/writer_test.go
 create mode 100644 credentialproviderpackage/pkg/templater/partialyaml.go
 create mode 100644 credentialproviderpackage/pkg/templater/partialyaml_test.go
 create mode 100644 credentialproviderpackage/pkg/templater/templater.go
 create mode 100644 credentialproviderpackage/pkg/templater/templater_test.go
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/key4_template.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/test1_template.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml
 create mode 100644 credentialproviderpackage/pkg/templater/yaml.go
 create mode 100644 credentialproviderpackage/pkg/utils/utils.go
 create mode 100644 credentialproviderpackage/skaffold.yaml

diff --git a/credentialproviderpackage/Dockerfile b/credentialproviderpackage/Dockerfile
new file mode 100644
index 00000000..89262061
--- /dev/null
+++ b/credentialproviderpackage/Dockerfile
@@ -0,0 +1,14 @@
+FROM golang:1.18-buster
+ENV GOTRACEBACK=single
+ENV GOPROXY=direct
+WORKDIR /app
+COPY go.mod .
+COPY go.sum .
+COPY cmd/ cmd/
+COPY ecr-credential-provider /eksa-binaries/
+COPY aws_signing_helper /eksa-binaries/
+COPY pkg/ pkg/
+ARG SKAFFOLD_GO_GCFLAGS
+RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o app cmd/aws-credential-provider/*.go
+
+CMD ["/app/app"]
\ No newline at end of file
diff --git a/credentialproviderpackage/Makefile b/credentialproviderpackage/Makefile
new file mode 100644
index 00000000..81b94688
--- /dev/null
+++ b/credentialproviderpackage/Makefile
@@ -0,0 +1,41 @@
+SHELL = /usr/bin/env bash -o pipefail
+.SHELLFLAGS = -ec
+
+REPO_ROOT=$(shell git rev-parse --show-toplevel)/credentialproviderpackage
+GOLANG_VERSION?="1.18"
+#GO ?= $(shell source $(REPO_ROOT)/scripts/common.sh && build::common::get_go_path $(GOLANG_VERSION))/go
+GO = go
+
+# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
+ifeq (,$(shell go env GOBIN))
+GOBIN=$(shell go env GOPATH)/bin
+else
+GOBIN=$(shell go env GOBIN)
+endif
+
+all: build
+
+help: ## Display this help.
+	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+clean: ## Clean output directory, and the built binary
+	rm -rf output/
+	rm -rf bin/*
+	rm cover.out
+
+##@ Build
+
+build: ## Build Binary
+	mkdir -p $(REPO_ROOT)/bin
+	$(GO) mod tidy -compat=$(GOLANG_VERSION)
+	$(GO) build -o $(REPO_ROOT)/bin/aws-credential-provider $(REPO_ROOT)/cmd/aws-credential-provider/*.go
+
+build-linux:
+	[ -d bin ] || mkdir bin
+	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(MAKE) build
+
+run:
+	$(GO) run .
+
+test: build
+	$(GO) test ./... `$(GO) list $(GOTESTS) | grep -v mocks | grep -v fake | grep -v testutil` -coverprofile cover.out
\ No newline at end of file
diff --git a/credentialproviderpackage/charts/credential-provider-package/.helmignore b/credentialproviderpackage/charts/credential-provider-package/.helmignore
new file mode 100644
index 00000000..0e8a0eb3
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/credentialproviderpackage/charts/credential-provider-package/Chart.yaml b/credentialproviderpackage/charts/credential-provider-package/Chart.yaml
new file mode 100644
index 00000000..499d9bf3
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: credential-provider-package
+description: A Helm chart for Kubernetes
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.16.0"
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt b/credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt
new file mode 100644
index 00000000..f9339199
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt
@@ -0,0 +1 @@
+1. Installed Credential Provider Package
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl b/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl
new file mode 100644
index 00000000..eb3e20b8
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl
@@ -0,0 +1,88 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "credential-provider.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "credential-provider.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "credential-provider.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "credential-provider.labels" -}}
+helm.sh/chart: {{ include "credential-provider.chart" . }}
+{{ include "credential-provider.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "credential-provider.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "credential-provider.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "credential-provider.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "credential-provider.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create image name
+*/}}
+{{- define "template.image" -}}
+{{- if eq (substr 0 7 .tag) "sha256:" -}}
+{{- printf "/%s@%s" .repository .tag -}}
+{{- else -}}
+{{- printf "/%s:%s" .repository .tag -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Function to figure out os name
+*/}}
+{{- define "template.getOSName" -}}
+{{- with first ((lookup "v1" "Node" "" "").items) -}}
+{{- if contains "Bottlerocket" .status.nodeInfo.osImage -}}
+{{- printf "bottlerocket" -}}
+{{- else if contains "Amazon Linux" .status.nodeInfo.osImage -}}
+{{- printf "docker" -}}
+{{- else -}}
+{{- printf "other" -}}
+{{- end }}
+{{- end }}
+{{- end }}
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml
new file mode 100644
index 00000000..bc98d2fa
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml
@@ -0,0 +1,89 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "credential-provider.fullname" . }}
+  namespace: {{ .Release.Namespace | default .Values.defaultNamespace | quote }}
+  labels:
+    {{- include "credential-provider.labels" . | nindent 4 }}
+spec:
+  selector:
+    matchLabels:
+      {{- include "credential-provider.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      {{- with .Values.podAnnotations }}
+      annotations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      labels:
+        {{- include "credential-provider.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      serviceAccountName: {{ include "credential-provider.serviceAccountName" . }}
+      securityContext:
+        {{- toYaml .Values.podSecurityContext | nindent 8 }}
+      containers:
+        - name: credential-provider
+          image: {{ .Values.sourceRegistry }}/{{ .Values.image.repository }}@{{ .Values.image.digest }}
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          securityContext:
+            privileged: true
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+          volumeMounts:
+            - name: aws-creds
+              mountPath: /secrets/aws-creds
+            {{ $os := include "template.getOSName" .}}
+            {{- if eq $os "bottlerocket" }}
+            - mountPath: /run/api.sock
+              name: socket
+            {{- else}}
+            - mountPath: /node-files/kubelet-extra-args
+              name: kubelet-extra-args
+            - name: package-mounts
+              mountPath: /eksa-packages
+            {{- end}}
+          env:
+            - name: OS_TYPE
+              value: {{ template "template.getOSName" }}
+      volumes:
+        - name: aws-creds
+          secret:
+            secretName: {{.Values.secretName}}
+            optional: false
+        {{- if eq $os "bottlerocket" }}
+        - name: socket
+          hostPath:
+            path: /run/api.sock
+        {{- else if eq $os "docker"}}
+        - name: kubelet-extra-args
+          hostPath:
+            path: /etc/default/kubelet
+            type: FileOrCreate
+        {{- else}}
+        - name: kubelet-extra-args
+          hostPath:
+            path: /etc/sysconfig/kubelet
+            type: FileOrCreate
+        {{- end }}
+        {{- if ne $os "bottlerocket" }}
+        - name: package-mounts
+          hostPath:
+            path: /eksa-packages
+        {{- end }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+      hostPID: true
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml
new file mode 100644
index 00000000..65ffbfdb
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "credential-provider.serviceAccountName" . }}
+  labels:
+    {{- include "credential-provider.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end }}
diff --git a/credentialproviderpackage/charts/credential-provider-package/values.yaml b/credentialproviderpackage/charts/credential-provider-package/values.yaml
new file mode 100644
index 00000000..78edfce9
--- /dev/null
+++ b/credentialproviderpackage/charts/credential-provider-package/values.yaml
@@ -0,0 +1,62 @@
+# Default values for credential-provider.
+# This is a YAML-formatted file.
+
+replicaCount: 1
+# -- sourceRegistry for all container images in chart.
+sourceRegistry: public.ecr.aws/eks-anywhere
+defaultNamespace: eksa-packages
+
+image:
+  repository: "credential-provider-package"
+  tag: "{{credential-provider-package-tag}}"
+  digest: sha256:320e4bdbe1cf0ccc4a594c84acc339cf4d652c40d84b5313f79ae68b9493db85
+  pullPolicy: IfNotPresent
+
+# application values
+secretName: aws-cred
+imagePatterns: ""
+defaultCacheDuration: ""
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: true
+  # Annotations to add to the service account
+  annotations: {}
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: {}
+
+podSecurityContext: {}
+  # fsGroup: 2000
+
+securityContext: {}
+  # capabilities:
+  #   drop:
+  #   - ALL
+  # readOnlyRootFilesystem: true
+  # runAsNonRoot: true
+  # runAsUser: 1000
+
+resources: {}
+  # We usually recommend not to specify default resources and to leave this as a conscious
+  # choice for the user. This also increases chances charts run on environments with little
+  # resources, such as Minikube. If you do want to specify resources, uncomment the following
+  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
new file mode 100644
index 00000000..2545ddb4
--- /dev/null
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -0,0 +1,119 @@
+package main
+
+import (
+	_ "embed"
+	"io/fs"
+	"log"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/fsnotify/fsnotify"
+
+	cfg "credential-provider/pkg/configurator"
+	"credential-provider/pkg/configurator/bottlerocket"
+	"credential-provider/pkg/configurator/linux"
+	"credential-provider/pkg/constants"
+	"credential-provider/pkg/utils"
+)
+
+func checkErrAndLog(err error, logger *log.Logger) {
+	if err != nil {
+		logger.Println(err)
+		os.Exit(0)
+	}
+}
+
+func main() {
+	utils.InfoLogger.Println("Running at " + time.Now().UTC().String())
+
+	var configurator cfg.Configurator
+	osType := strings.ToLower(os.Getenv("OS_TYPE"))
+	if osType == "" {
+		utils.ErrorLogger.Println("Missing Environment Variable OS")
+		os.Exit(0)
+	}
+	config := createCredentialProviderConfigOptions()
+	if osType == constants.BottleRocket {
+		socket, err := os.Stat(constants.SocketPath)
+		checkErrAndLog(err, utils.ErrorLogger)
+		if socket.Mode().Type() == fs.ModeSocket {
+			configurator = bottlerocket.NewBottleRocketConfigurator()
+			configurator.Initialize(constants.SocketPath, config)
+		} else {
+			utils.ErrorLogger.Printf("Unexpected type %s expected socket\n", socket.Mode().Type())
+			os.Exit(0)
+		}
+	} else {
+		configurator = linux.NewLinuxConfigurator()
+		configurator.Initialize("", config)
+	}
+
+	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, constants.Profile)
+	checkErrAndLog(err, utils.ErrorLogger)
+	utils.InfoLogger.Println("Aws credentials configured")
+
+	err = configurator.UpdateCredentialProvider(constants.Profile)
+	checkErrAndLog(err, utils.ErrorLogger)
+	utils.InfoLogger.Println("Credential Provider Configured")
+
+	err = configurator.CommitChanges()
+	checkErrAndLog(err, utils.ErrorLogger)
+
+	utils.InfoLogger.Println("Kubelet Restarted")
+
+	// Creating watcher for credentials
+	watcher, err := fsnotify.NewWatcher()
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer watcher.Close()
+
+	// Start listening for changes to the aws credentials
+	go func() {
+		for {
+			select {
+			case event, ok := <-watcher.Events:
+				if !ok {
+					return
+				}
+				if event.Has(fsnotify.Create) {
+					if event.Name == constants.CredWatchData {
+						err = configurator.UpdateAWSCredentials(constants.CredSrcPath, constants.Profile)
+						checkErrAndLog(err, utils.ErrorLogger)
+						utils.InfoLogger.Println("Aws credentials successfully changed")
+					}
+				}
+			case err, ok := <-watcher.Errors:
+				if !ok {
+					return
+				}
+				log.Println("error:", err)
+			}
+		}
+	}()
+
+	err = watcher.Add(constants.CredWatchPath)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Block main goroutine forever.
+	<-make(chan struct{})
+}
+
+func createCredentialProviderConfigOptions() constants.CredentialProviderConfigOptions {
+	imagePatterns := os.Getenv("IMAGE_PATTERNS")
+	if imagePatterns == "" {
+		imagePatterns = constants.ImagePattern
+	}
+	defaultCacheDuration := os.Getenv("DEFAULT_CACHE_DURATION")
+	if defaultCacheDuration == "" {
+		defaultCacheDuration = constants.CacheDuration
+	}
+
+	return constants.CredentialProviderConfigOptions{
+		ImagePatterns:        imagePatterns,
+		DefaultCacheDuration: defaultCacheDuration,
+	}
+}
diff --git a/credentialproviderpackage/go.mod b/credentialproviderpackage/go.mod
new file mode 100644
index 00000000..98f1d575
--- /dev/null
+++ b/credentialproviderpackage/go.mod
@@ -0,0 +1,28 @@
+module credential-provider
+
+go 1.18
+
+require (
+	github.com/fsnotify/fsnotify v1.6.0
+	github.com/google/go-cmp v0.5.9
+	github.com/mitchellh/go-ps v1.0.0
+	github.com/stretchr/testify v1.8.0
+	k8s.io/apimachinery v0.26.0
+	sigs.k8s.io/yaml v1.3.0
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/go-logr/logr v1.2.3 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/sys v0.4.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	k8s.io/klog/v2 v2.80.1 // indirect
+	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+	sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
+)
diff --git a/credentialproviderpackage/go.sum b/credentialproviderpackage/go.sum
new file mode 100644
index 00000000..82b56f68
--- /dev/null
+++ b/credentialproviderpackage/go.sum
@@ -0,0 +1,87 @@
+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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
+github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
+k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
+k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
+k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
+sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/credentialproviderpackage/internal/test/files.go b/credentialproviderpackage/internal/test/files.go
new file mode 100644
index 00000000..d654aecc
--- /dev/null
+++ b/credentialproviderpackage/internal/test/files.go
@@ -0,0 +1,137 @@
+package test
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"regexp"
+	"testing"
+
+	"credential-provider/pkg/filewriter"
+)
+
+var UpdateGoldenFiles = flag.Bool("update", false, "update golden files")
+
+func AssertFilesEquals(t *testing.T, gotPath, wantPath string) {
+	t.Helper()
+	gotFile := ReadFile(t, gotPath)
+	processUpdate(t, wantPath, gotFile)
+	wantFile := ReadFile(t, wantPath)
+
+	if gotFile != wantFile {
+		cmd := exec.Command("diff", wantPath, gotPath)
+		result, err := cmd.Output()
+		if err != nil {
+			if exitError, ok := err.(*exec.ExitError); ok {
+				if exitError.ExitCode() == 1 {
+					t.Fatalf("Results diff expected actual:\n%s", string(result))
+				}
+			}
+		}
+		t.Fatalf("Files are different got =\n  %s \n want =\n %s\n%s", gotFile, wantFile, err)
+	}
+}
+
+func AssertContentToFile(t *testing.T, gotContent, wantFile string) {
+	t.Helper()
+	if wantFile == "" {
+		return
+	}
+	processUpdate(t, wantFile, gotContent)
+
+	fileContent := ReadFile(t, wantFile)
+	if gotContent != fileContent {
+		diff, err := computeDiffBetweenContentAndFile([]byte(gotContent), wantFile)
+		if err != nil {
+			t.Fatalf("Content doesn't match file got =\n%s\n\n\nwant =\n%s\n", gotContent, fileContent)
+		}
+		if diff != "" {
+			t.Fatalf("Results diff expected actual for %s:\n%s", wantFile, string(diff))
+		}
+	}
+}
+
+func contentEqualToFile(gotContent []byte, wantFile string) (bool, error) {
+	if wantFile == "" && len(gotContent) == 0 {
+		return false, nil
+	}
+
+	fileContent, err := ioutil.ReadFile(wantFile)
+	if err != nil {
+		return false, err
+	}
+
+	return bytes.Equal(gotContent, fileContent), nil
+}
+
+func computeDiffBetweenContentAndFile(content []byte, file string) (string, error) {
+	cmd := exec.Command("diff", "-u", file, "-")
+	cmd.Stdin = bytes.NewReader([]byte(content))
+	result, err := cmd.Output()
+	if err != nil {
+		if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
+			return string(result), nil
+		}
+
+		return "", fmt.Errorf("computing the difference between content and file %s: %v", file, err)
+	}
+	return "", nil
+}
+
+func processUpdate(t *testing.T, filePath, content string) {
+	if *UpdateGoldenFiles {
+		if err := ioutil.WriteFile(filePath, []byte(content), 0o644); err != nil {
+			t.Fatalf("failed to update golden file %s: %v", filePath, err)
+		}
+		log.Printf("Golden file updated: %s", filePath)
+	}
+}
+
+func ReadFileAsBytes(t *testing.T, file string) []byte {
+	bytesRead, err := ioutil.ReadFile(file)
+	if err != nil {
+		t.Fatalf("File [%s] reading error in test: %v", file, err)
+	}
+
+	return bytesRead
+}
+
+func ReadFile(t *testing.T, file string) string {
+	return string(ReadFileAsBytes(t, file))
+}
+
+func NewWriter(t *testing.T) (dir string, writer filewriter.FileWriter) {
+	dir, err := ioutil.TempDir(".", SanitizePath(t.Name())+"-")
+	if err != nil {
+		t.Fatalf("error setting up folder for test: %v", err)
+	}
+
+	t.Cleanup(cleanupDir(t, dir))
+	writer, err = filewriter.NewWriter(dir)
+	if err != nil {
+		t.Fatalf("error creating writer with folder for test: %v", err)
+	}
+	return dir, writer
+}
+
+func cleanupDir(t *testing.T, dir string) func() {
+	return func() {
+		if !t.Failed() {
+			os.RemoveAll(dir)
+		}
+	}
+}
+
+var sanitizePathChars = regexp.MustCompile(`[^\w-]`)
+
+const sanitizePathReplacementChar = "_"
+
+// SanitizePath sanitizes s so its usable as a path name. For safety, it assumes all characters that are not
+// A-Z, a-z, 0-9, _ or - are illegal and replaces them with _.
+func SanitizePath(s string) string {
+	return sanitizePathChars.ReplaceAllString(s, sanitizePathReplacementChar)
+}
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
new file mode 100644
index 00000000..d39c0fb4
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
@@ -0,0 +1,171 @@
+package bottlerocket
+
+import (
+	"bytes"
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+
+	"credential-provider/pkg/configurator"
+	"credential-provider/pkg/constants"
+)
+
+type bottleRocket struct {
+	client  http.Client
+	baseURL string
+	config  constants.CredentialProviderConfigOptions
+}
+
+type AwsCred struct {
+	Aws Aws `json:"aws"`
+}
+type Aws struct {
+	Config  string `json:"config"`
+	Profile string `json:"profile"`
+	Region  string `json:"region"`
+}
+
+type brKubernetes struct {
+	Kubernetes kubernetes `json:"kubernetes"`
+}
+type ecrCredentialProvider struct {
+	CacheDuration string   `json:"cache-duration"`
+	Enabled       bool     `json:"enabled"`
+	ImagePatterns []string `json:"image-patterns"`
+}
+type credentialProviders struct {
+	EcrCredentialProvider ecrCredentialProvider `json:"ecr-credential-provider"`
+}
+type kubernetes struct {
+	CredentialProviders credentialProviders `json:"credential-providers"`
+}
+
+var _ configurator.Configurator = (*bottleRocket)(nil)
+
+func NewBottleRocketConfigurator() *bottleRocket {
+	return &bottleRocket{}
+}
+
+func (b *bottleRocket) Initialize(socketPath string, config constants.CredentialProviderConfigOptions) {
+	b.baseURL = "http://localhost/"
+	b.config = config
+	b.client = http.Client{
+		Transport: &http.Transport{
+			DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
+				return net.Dial("unix", socketPath)
+			},
+		},
+	}
+}
+
+func (b *bottleRocket) UpdateAWSCredentials(path string, profile string) error {
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		return err
+	}
+
+	content := base64.StdEncoding.EncodeToString(data)
+	payload, err := createCredentialsPayload(content, profile)
+	if err != nil {
+		return err
+	}
+	err = b.sendSettingsSetRequest(payload)
+	if err != nil {
+		return err
+	}
+
+	err = b.CommitChanges()
+	if err != nil {
+		return err
+	}
+
+	return err
+}
+
+func (b *bottleRocket) UpdateCredentialProvider(_ string) error {
+	payload, err := createCredentialProviderPayload(b.config)
+	if err != nil {
+		return err
+	}
+	err = b.sendSettingsSetRequest(payload)
+	if err != nil {
+		return err
+	}
+
+	return err
+}
+
+func (b *bottleRocket) CommitChanges() error {
+	// For Bottlerocket this step is committing all changes at once
+	commitPath := b.baseURL + "tx/commit_and_apply"
+	resp, err := b.client.Post(commitPath, "application/json", bytes.NewBuffer(make([]byte, 0)))
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("failed to commit changes: %s", resp.Status)
+	}
+	return nil
+}
+
+func (b *bottleRocket) sendSettingsSetRequest(payload []byte) error {
+	settingsPath := b.baseURL + "settings"
+	req, err := http.NewRequest(http.MethodPatch, settingsPath, bytes.NewBuffer(payload))
+	if err != nil {
+		return err
+	}
+
+	req.Header.Set("Content-Type", "application/json")
+	respPatch, err := b.client.Do(req)
+	if err != nil {
+		return err
+	}
+	defer respPatch.Body.Close()
+
+	if respPatch.StatusCode != http.StatusNoContent {
+		return fmt.Errorf("failed patch request: %s", respPatch.Status)
+	}
+
+	return nil
+
+}
+
+func createCredentialsPayload(content string, profile string) ([]byte, error) {
+	aws := Aws{
+		Config:  content,
+		Profile: profile,
+	}
+
+	creds := AwsCred{Aws: aws}
+
+	payload, err := json.Marshal(creds)
+	if err != nil {
+		return nil, err
+	}
+	return payload, nil
+}
+
+func createCredentialProviderPayload(config constants.CredentialProviderConfigOptions) ([]byte, error) {
+	providerConfig := brKubernetes{
+		Kubernetes: kubernetes{
+			credentialProviders{
+				ecrCredentialProvider{
+					Enabled:       true,
+					ImagePatterns: []string{config.ImagePatterns},
+					CacheDuration: config.DefaultCacheDuration,
+				},
+			},
+		},
+	}
+
+	payload, err := json.Marshal(providerConfig)
+	if err != nil {
+		return nil, err
+	}
+	return payload, nil
+}
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
new file mode 100644
index 00000000..f76e9511
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
@@ -0,0 +1,323 @@
+package bottlerocket
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"credential-provider/pkg/constants"
+)
+
+type response struct {
+	statusCode   int
+	expectedBody []byte
+	responseMsg  string
+}
+
+func Test_bottleRocket_CommitChanges(t *testing.T) {
+	type fields struct {
+		client  http.Client
+		baseURL string
+		config  constants.CredentialProviderConfigOptions
+	}
+
+	tests := []struct {
+		name     string
+		fields   fields
+		wantErr  bool
+		response response
+		expected string
+	}{
+		{
+			name: "test success",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			wantErr: false,
+			response: response{
+				statusCode:  http.StatusOK,
+				responseMsg: "",
+			},
+		},
+		{
+			name: "test fail",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			wantErr: true,
+			response: response{
+				statusCode:  http.StatusNotFound,
+				responseMsg: "",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+				w.WriteHeader(tt.response.statusCode)
+				fmt.Fprintf(w, tt.response.responseMsg)
+			}))
+			b := &bottleRocket{
+				client:  tt.fields.client,
+				baseURL: svr.URL + "/",
+				config:  tt.fields.config,
+			}
+			if err := b.CommitChanges(); (err != nil) != tt.wantErr {
+				t.Errorf("UpdateAWSCredentials() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_bottleRocket_UpdateAWSCredentials(t *testing.T) {
+	type fields struct {
+		client  http.Client
+		baseURL string
+		config  constants.CredentialProviderConfigOptions
+	}
+	type args struct {
+		path    string
+		profile string
+	}
+
+	tests := []struct {
+		name           string
+		fields         fields
+		args           args
+		patchResponse  response
+		commitResponse response
+		wantErr        bool
+	}{
+		{
+			name: "working credential update",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{},
+			},
+			args: args{
+				path:    "testdata/testcreds",
+				profile: "eksa-packages",
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNoContent,
+				expectedBody: []byte("{\"aws\":{\"config\":\"W3Byb2ZpbGUgZWtzYS1wYWNrYWdlc10KYXdzX2FjY2Vzc19rZXlfaWQ9QUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5PXdKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVk=\",\"profile\":\"eksa-packages\",\"region\":\"\"}}"),
+				responseMsg:  "",
+			},
+			commitResponse: response{
+				statusCode:  http.StatusOK,
+				responseMsg: "",
+			},
+			wantErr: false,
+		},
+		{
+			name: "commit credentials failed",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{},
+			},
+			args: args{
+				path:    "testdata/testcreds",
+				profile: "eksa-packages",
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNoContent,
+				expectedBody: []byte("{\"aws\":{\"config\":\"W3Byb2ZpbGUgZWtzYS1wYWNrYWdlc10KYXdzX2FjY2Vzc19rZXlfaWQ9QUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5PXdKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVk=\",\"profile\":\"eksa-packages\",\"region\":\"\"}}"),
+				responseMsg:  "",
+			},
+			commitResponse: response{
+				statusCode:  http.StatusNotFound,
+				responseMsg: "",
+			},
+			wantErr: true,
+		},
+		{
+			name: "failed to patch data",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{},
+			},
+			args: args{
+				path:    "testdata/testcreds",
+				profile: "eksa-packages",
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNotFound,
+				expectedBody: []byte("{\"aws\":{\"config\":\"W3Byb2ZpbGUgZWtzYS1wYWNrYWdlc10KYXdzX2FjY2Vzc19rZXlfaWQ9QUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5PXdKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVk=\",\"profile\":\"eksa-packages\",\"region\":\"\"}}"),
+				responseMsg:  "",
+			},
+			commitResponse: response{
+				statusCode:  http.StatusOK,
+				responseMsg: "",
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			svr := httptest.NewServer(http.HandlerFunc(
+				func(w http.ResponseWriter, r *http.Request) {
+					if r.Method == http.MethodPatch {
+						validatePatchRequest(w, r, t, tt.patchResponse)
+					} else if r.Method == http.MethodPost {
+						w.WriteHeader(tt.commitResponse.statusCode)
+						fmt.Fprintf(w, tt.commitResponse.responseMsg)
+					} else {
+						t.Errorf("Recieved unexected request %v", r.Method)
+					}
+				}),
+			)
+			b := &bottleRocket{
+				client:  tt.fields.client,
+				baseURL: svr.URL + "/",
+				config:  tt.fields.config,
+			}
+			if err := b.UpdateAWSCredentials(tt.args.path, tt.args.profile); (err != nil) != tt.wantErr {
+				t.Errorf("UpdateAWSCredentials() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_bottleRocket_UpdateCredentialProvider(t *testing.T) {
+	type fields struct {
+		client  http.Client
+		baseURL string
+		config  constants.CredentialProviderConfigOptions
+	}
+
+	tests := []struct {
+		name          string
+		fields        fields
+		patchResponse response
+		wantErr       bool
+	}{
+		{
+			name: "default credential provider",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNoContent,
+				expectedBody: []byte("{\"kubernetes\":{\"credential-providers\":{\"ecr-credential-provider\":{\"cache-duration\":\"30m\",\"enabled\":true,\"image-patterns\":[\"*.dkr.ecr.*.amazonaws.com\"]}}}}"),
+				responseMsg:  "",
+			},
+			wantErr: false,
+		},
+		{
+			name: "non default values for credential provider",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        "123456789.dkr.ecr.test-region.amazonaws.com",
+					DefaultCacheDuration: "24h",
+				},
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNoContent,
+				expectedBody: []byte("{\"kubernetes\":{\"credential-providers\":{\"ecr-credential-provider\":{\"cache-duration\":\"24h\",\"enabled\":true,\"image-patterns\":[\"123456789.dkr.ecr.test-region.amazonaws.com\"]}}}}"),
+				responseMsg:  "",
+			},
+			wantErr: false,
+		},
+		{
+			name: "failed credential provider update",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNotFound,
+				expectedBody: []byte("{\"kubernetes\":{\"credential-providers\":{\"ecr-credential-provider\":{\"cache-duration\":\"30m\",\"enabled\":true,\"image-patterns\":[\"*.dkr.ecr.*.amazonaws.com\"]}}}}"),
+				responseMsg:  "",
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			svr := httptest.NewServer(http.HandlerFunc(
+				func(w http.ResponseWriter, r *http.Request) {
+					if r.Method == http.MethodPatch {
+						validatePatchRequest(w, r, t, tt.patchResponse)
+					} else {
+						t.Errorf("Recieved unexected request %v", r.Method)
+					}
+				}),
+			)
+
+			b := &bottleRocket{
+				client:  tt.fields.client,
+				baseURL: svr.URL + "/",
+				config:  tt.fields.config,
+			}
+			if err := b.UpdateCredentialProvider(""); (err != nil) != tt.wantErr {
+				t.Errorf("UpdateCredentialProvider() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func validatePatchRequest(w http.ResponseWriter, r *http.Request, t *testing.T, patchResponse response) {
+	data, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		t.Errorf("failed to read response")
+	}
+	if !bytes.Equal(data, patchResponse.expectedBody) {
+		t.Errorf("Patch message expcted %v got %v", patchResponse.expectedBody, data)
+	}
+	w.WriteHeader(patchResponse.statusCode)
+	fmt.Fprintf(w, patchResponse.responseMsg)
+}
+
+func Test_bottleRocket_Initialize(t *testing.T) {
+	type args struct {
+		socketPath string
+		config     constants.CredentialProviderConfigOptions
+	}
+	tests := []struct {
+		name    string
+		baseUrl string
+		args    args
+	}{
+		{
+			name:    "simple initialization",
+			baseUrl: "http://localhost/",
+			args: args{
+				socketPath: "/test/path.sock",
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b := NewBottleRocketConfigurator()
+			b.Initialize(tt.args.socketPath, tt.args.config)
+			assert.Equal(t, tt.baseUrl, b.baseURL)
+			assert.Equal(t, tt.args.config, b.config)
+			assert.NotNil(t, b.client)
+		})
+	}
+}
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds b/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds
new file mode 100644
index 00000000..cec03763
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds
@@ -0,0 +1,3 @@
+[profile eksa-packages]
+aws_access_key_id=AKIAIOSFODNN7EXAMPLE
+aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/configurator/configurator.go b/credentialproviderpackage/pkg/configurator/configurator.go
new file mode 100644
index 00000000..04f160bf
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/configurator.go
@@ -0,0 +1,17 @@
+package configurator
+
+import "credential-provider/pkg/constants"
+
+type Configurator interface {
+	// Initialize Handles node specific configuration depending on OS
+	Initialize(filepath string, config constants.CredentialProviderConfigOptions)
+
+	// UpdateAWSCredentials Handles AWS Credential Setup
+	UpdateAWSCredentials(sourcePath string, profile string) error
+
+	// UpdateCredentialProvider Handles Credential Provider Setup
+	UpdateCredentialProvider(profile string) error
+
+	// CommitChanges Applies changes to Kubelet
+	CommitChanges() error
+}
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
new file mode 100644
index 00000000..9fef9088
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -0,0 +1,223 @@
+package linux
+
+import (
+	_ "embed"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+	"syscall"
+
+	ps "github.com/mitchellh/go-ps"
+
+	"credential-provider/pkg/configurator"
+	"credential-provider/pkg/constants"
+	"credential-provider/pkg/templater"
+	"credential-provider/pkg/utils"
+)
+
+//go:embed templates/credential-provider-config.yaml
+var credProviderTemplate string
+
+type linuxOS struct {
+	profile       string
+	extraArgsPath string
+	basePath      string
+	config        constants.CredentialProviderConfigOptions
+}
+
+var _ configurator.Configurator = (*linuxOS)(nil)
+
+func NewLinuxConfigurator() *linuxOS {
+	return &linuxOS{
+		profile:       "",
+		extraArgsPath: constants.MountedExtraArgs,
+		basePath:      constants.BasePath,
+	}
+}
+
+func (c *linuxOS) Initialize(_ string, config constants.CredentialProviderConfigOptions) {
+	c.config = config
+}
+
+func (c *linuxOS) UpdateAWSCredentials(sourcePath string, profile string) error {
+	c.profile = profile
+	dstPath := c.basePath + constants.CredOutFile
+
+	err := copyWithPermissons(sourcePath, dstPath, 0600)
+	return err
+}
+
+func (c *linuxOS) UpdateCredentialProvider(_ string) error {
+	// Adding to KUBELET_EXTRA_ARGS in place
+	file, err := ioutil.ReadFile(c.extraArgsPath)
+	if err != nil {
+		return err
+	}
+
+	lines := strings.Split(string(file), "\n")
+	found := false
+	for i, line := range lines {
+		if strings.HasPrefix(line, "KUBELET_EXTRA_ARGS") {
+			found = true
+			args := c.updateKubeletArguments(line)
+
+			if args != "" {
+				lines[i] = line + args + "\n"
+			}
+		}
+	}
+	if !found {
+		line := "KUBELET_EXTRA_ARGS="
+		args := c.updateKubeletArguments(line)
+		if args != "" {
+			line = line + args
+		}
+		lines = append(lines, line)
+	}
+
+	out := strings.Join(lines, "\n")
+	err = ioutil.WriteFile(c.extraArgsPath, []byte(out), 0644)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (c *linuxOS) CommitChanges() error {
+	process, err := findKubeletProcess()
+	if err != nil {
+		return err
+	}
+	err = killProcess(process)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func killProcess(process ps.Process) error {
+	err := syscall.Kill(process.Pid(), syscall.SIGHUP)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func findKubeletProcess() (ps.Process, error) {
+	processList, err := ps.Processes()
+	if err != nil {
+		return nil, err
+	}
+	for x := range processList {
+		process := processList[x]
+		if process.Executable() == "kubelet" {
+			return process, nil
+		}
+	}
+	return nil, fmt.Errorf("cannot find Kubelet Process")
+}
+
+func copyWithPermissons(srcpath, dstpath string, permission os.FileMode) (err error) {
+	r, err := os.Open(srcpath)
+	if err != nil {
+		return err
+	}
+	defer r.Close() // ok to ignore error: file was opened read-only.
+
+	w, err := os.Create(dstpath)
+	if err != nil {
+		return err
+	}
+
+	defer func() {
+		c := w.Close()
+		// Report the error from Close, if any.
+		// But do so only if there isn't already
+		// an outgoing error.
+		if c != nil && err == nil {
+			err = c
+		}
+	}()
+
+	_, err = io.Copy(w, r)
+	if err != nil {
+		return err
+	}
+	err = os.Chmod(dstpath, permission)
+	return err
+}
+
+func copyBinaries() (string, error) {
+	srcPath := constants.BinPath + constants.ECRCredProviderBinary
+	dstPath := constants.BasePath + constants.ECRCredProviderBinary
+	err := copyWithPermissons(srcPath, dstPath, 0744)
+	if err != nil {
+		return "", err
+	}
+
+	err = os.Chmod(dstPath, 0744)
+	if err != nil {
+		return "", err
+	}
+
+	srcPath = constants.BinPath + constants.IAMRolesSigningBinary
+	dstPath = constants.BasePath + constants.IAMRolesSigningBinary
+	err = copyWithPermissons(srcPath, dstPath, 0744)
+	if err != nil {
+		return "", err
+	}
+
+	err = os.Chmod(dstPath, 0744)
+	if err != nil {
+		return "", err
+	}
+	return fmt.Sprintf(" --image-credential-provider-bin-dir=%s", constants.BasePath), nil
+}
+
+func (c *linuxOS) createConfig() (string, error) {
+	values := map[string]interface{}{
+		"profile":       c.profile,
+		"config":        constants.BasePath + constants.CredOutFile,
+		"home":          constants.BasePath,
+		"imagePattern":  c.config.ImagePatterns,
+		"cacheDuration": c.config.DefaultCacheDuration,
+	}
+
+	dstPath := c.basePath + constants.CredProviderFile
+
+	bytes, err := templater.Execute(credProviderTemplate, values)
+	if err != nil {
+		return "", nil
+	}
+	err = ioutil.WriteFile(dstPath, bytes, 0644)
+	if err != nil {
+		return "", err
+	}
+	return fmt.Sprintf(" --image-credential-provider-config=%s", dstPath), nil
+}
+
+func (c *linuxOS) updateKubeletArguments(line string) string {
+	args := ""
+	if !strings.Contains(line, "KubeletCredentialProviders") {
+		args += " --feature-gates=KubeletCredentialProviders=true"
+	}
+
+	if !strings.Contains(line, "image-credential-provider-config") {
+		val, err := c.createConfig()
+		if err != nil {
+			utils.ErrorLogger.Printf("Error creating configuration %v", err)
+		}
+		args += val
+
+		if !strings.Contains(line, "image-credential-provider-bin-dir") {
+			val, err = copyBinaries()
+			if err != nil {
+				utils.ErrorLogger.Printf("Error coping binaries %v\n", err)
+			}
+			args += val
+		}
+	}
+	return args
+}
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux_test.go b/credentialproviderpackage/pkg/configurator/linux/linux_test.go
new file mode 100644
index 00000000..6430b012
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/linux/linux_test.go
@@ -0,0 +1,212 @@
+package linux
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"credential-provider/internal/test"
+	"credential-provider/pkg/constants"
+)
+
+func Test_linuxOS_updateKubeletArguments(t *testing.T) {
+	testDir, _ := test.NewWriter(t)
+	dir := testDir + "/"
+	type fields struct {
+		profile       string
+		extraArgsPath string
+		basePath      string
+		config        constants.CredentialProviderConfigOptions
+	}
+	type args struct {
+		line string
+	}
+	tests := []struct {
+		name             string
+		fields           fields
+		args             args
+		outputConfigPath string
+		configWantPath   string
+		want             string
+	}{
+		{
+			name: "test empty string",
+			fields: fields{
+				profile:       "eksa-packages",
+				extraArgsPath: dir,
+				basePath:      dir,
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			args:             args{line: ""},
+			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			configWantPath:   "testdata/expected-config.yaml",
+			want: fmt.Sprintf(" --feature-gates=KubeletCredentialProviders=true "+
+				"--image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
+		},
+		{
+			name: "skip credential provider if already provided",
+			fields: fields{
+				profile:       "eksa-packages",
+				extraArgsPath: dir,
+				basePath:      dir,
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			args:             args{line: " --feature-gates=KubeletCredentialProviders=true"},
+			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			configWantPath:   "testdata/expected-config.yaml",
+			want:             fmt.Sprintf(" --image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
+		},
+		{
+			name: "skip both cred provider and feature gate if provided",
+			fields: fields{
+				profile:       "eksa-packages",
+				extraArgsPath: dir,
+				basePath:      dir,
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			args:             args{line: " --feature-gates=KubeletCredentialProviders=false --image-credential-provider-config=blah"},
+			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			configWantPath:   "",
+			want:             "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := &linuxOS{
+				profile:       tt.fields.profile,
+				extraArgsPath: tt.fields.extraArgsPath,
+				basePath:      tt.fields.basePath,
+				config:        tt.fields.config,
+			}
+			if got := c.updateKubeletArguments(tt.args.line); got != tt.want {
+				t.Errorf("updateKubeletArguments() = %v, want %v", got, tt.want)
+			}
+			if tt.configWantPath != "" {
+				test.AssertFilesEquals(t, tt.outputConfigPath, tt.configWantPath)
+			}
+
+		})
+	}
+}
+
+func Test_linuxOS_UpdateAWSCredentials(t *testing.T) {
+	testDir, _ := test.NewWriter(t)
+	dir := testDir + "/"
+	type fields struct {
+		profile       string
+		extraArgsPath string
+		basePath      string
+		config        constants.CredentialProviderConfigOptions
+	}
+	type args struct {
+		sourcePath string
+		profile    string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "simple credential move",
+			fields: fields{
+				profile:       "eksa-packages",
+				extraArgsPath: dir,
+				basePath:      dir,
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+			args: args{
+				sourcePath: "testdata/testcreds",
+				profile:    "eksa-packages",
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			dstFile := tt.fields.basePath + constants.CredOutFile
+			c := &linuxOS{
+				profile:       tt.fields.profile,
+				extraArgsPath: tt.fields.extraArgsPath,
+				basePath:      tt.fields.basePath,
+				config:        tt.fields.config,
+			}
+			if err := c.UpdateAWSCredentials(tt.args.sourcePath, tt.args.profile); (err != nil) != tt.wantErr {
+				t.Errorf("UpdateAWSCredentials() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			info, err := os.Stat(dstFile)
+			if err != nil {
+				t.Errorf("Failed to open destination file")
+			}
+			if info.Mode().Perm() != os.FileMode(0600) {
+				t.Errorf("Credential file not saved with correct permission")
+			}
+
+			if err != nil {
+				t.Errorf("Failed to set file back to readable")
+			}
+			expectedCreds, err := ioutil.ReadFile(tt.args.sourcePath)
+			if err != nil {
+				t.Errorf("Failed to read source credential file")
+			}
+
+			actualCreds, err := ioutil.ReadFile(dstFile)
+			if err != nil {
+				t.Errorf("Failed to read created credential file")
+			}
+			assert.Equal(t, expectedCreds, actualCreds)
+		})
+	}
+}
+
+func Test_linuxOS_Initialize(t *testing.T) {
+	type fields struct {
+		profile       string
+		extraArgsPath string
+		basePath      string
+		config        constants.CredentialProviderConfigOptions
+	}
+	type args struct {
+		in0    string
+		config constants.CredentialProviderConfigOptions
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		args   args
+	}{
+		{
+			name: "simple initialization",
+			args: args{
+				in0: "",
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        constants.ImagePattern,
+					DefaultCacheDuration: constants.CacheDuration,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := NewLinuxConfigurator()
+			c.Initialize(tt.args.in0, tt.args.config)
+			assert.Equal(t, c.config, tt.args.config)
+		})
+	}
+}
diff --git a/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml b/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml
new file mode 100644
index 00000000..4701c6f2
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml
@@ -0,0 +1,17 @@
+apiVersion: kubelet.config.k8s.io/v1alpha1
+kind: CredentialProviderConfig
+providers:
+  - name: ecr-credential-provider
+    matchImages:
+      - "{{.imagePattern}}"
+    defaultCacheDuration: "{{.cacheDuration}}"
+    apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
+    env:
+      - name: AWS_PROFILE
+        value: {{.profile}}
+      - name: AWS_CONFIG_FILE
+        value: {{.config}}
+      - name: PATH
+        value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/eksa-packages
+      - name: HOME
+        value: {{.home}}
diff --git a/credentialproviderpackage/pkg/configurator/linux/testdata/expected-config.yaml b/credentialproviderpackage/pkg/configurator/linux/testdata/expected-config.yaml
new file mode 100644
index 00000000..2c7ffcd1
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/linux/testdata/expected-config.yaml
@@ -0,0 +1,17 @@
+apiVersion: kubelet.config.k8s.io/v1alpha1
+kind: CredentialProviderConfig
+providers:
+  - name: ecr-credential-provider
+    matchImages:
+      - "*.dkr.ecr.*.amazonaws.com"
+    defaultCacheDuration: "30m"
+    apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
+    env:
+      - name: AWS_PROFILE
+        value: eksa-packages
+      - name: AWS_CONFIG_FILE
+        value: /eksa-packages/aws-creds
+      - name: PATH
+        value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/eksa-packages
+      - name: HOME
+        value: /eksa-packages/
diff --git a/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds b/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds
new file mode 100644
index 00000000..cec03763
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds
@@ -0,0 +1,3 @@
+[profile eksa-packages]
+aws_access_key_id=AKIAIOSFODNN7EXAMPLE
+aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/constants/constants.go b/credentialproviderpackage/pkg/constants/constants.go
new file mode 100644
index 00000000..ec272ad5
--- /dev/null
+++ b/credentialproviderpackage/pkg/constants/constants.go
@@ -0,0 +1,41 @@
+package constants
+
+const (
+	// Credential Provider constants
+	ImagePattern     = "*.dkr.ecr.*.amazonaws.com"
+	CacheDuration    = "30m"
+	CredProviderFile = "credential-provider-config.yaml"
+
+	// Aws Credentials
+	CredSrcPath   = "/secrets/aws-creds/config"
+	Profile       = "eksa-packages"
+	CredWatchData = "/secrets/aws-creds/..data"
+	CredWatchPath = "/secrets/aws-creds/"
+
+	// BottleRocket
+	SocketPath = "/run/api.sock"
+
+	// Linux
+	BinPath          = "/eksa-binaries/"
+	BasePath         = "/eksa-packages/"
+	CredOutFile      = "aws-creds"
+	MountedExtraArgs = "/node-files/kubelet-extra-args"
+
+	// Binaries
+	ECRCredProviderBinary = "ecr-credential-provider"
+	IAMRolesSigningBinary = "aws_signing_helper"
+)
+
+type OSType string
+
+const (
+	Docker       OSType = "docker"
+	Ubuntu              = "ubuntu"
+	Redhat              = "redhat"
+	BottleRocket        = "bottlerocket"
+)
+
+type CredentialProviderConfigOptions struct {
+	ImagePatterns        string
+	DefaultCacheDuration string
+}
diff --git a/credentialproviderpackage/pkg/filewriter/filewriter.go b/credentialproviderpackage/pkg/filewriter/filewriter.go
new file mode 100644
index 00000000..dde7c8a8
--- /dev/null
+++ b/credentialproviderpackage/pkg/filewriter/filewriter.go
@@ -0,0 +1,23 @@
+package filewriter
+
+import (
+	"io"
+	"os"
+)
+
+type FileWriter interface {
+	Write(fileName string, content []byte, f ...FileOptionsFunc) (path string, err error)
+	WithDir(dir string) (FileWriter, error)
+	CleanUp()
+	CleanUpTemp()
+	Dir() string
+	TempDir() string
+	Create(name string, f ...FileOptionsFunc) (_ io.WriteCloser, path string, _ error)
+}
+
+type FileOptions struct {
+	IsTemp      bool
+	Permissions os.FileMode
+}
+
+type FileOptionsFunc func(op *FileOptions)
diff --git a/credentialproviderpackage/pkg/filewriter/filewriter_defaults.go b/credentialproviderpackage/pkg/filewriter/filewriter_defaults.go
new file mode 100644
index 00000000..9288400d
--- /dev/null
+++ b/credentialproviderpackage/pkg/filewriter/filewriter_defaults.go
@@ -0,0 +1,19 @@
+package filewriter
+
+import (
+	"os"
+)
+
+const DefaultTmpFolder = "generated"
+
+func defaultFileOptions() *FileOptions {
+	return &FileOptions{true, os.ModePerm}
+}
+
+func Permission0600(op *FileOptions) {
+	op.Permissions = 0o600
+}
+
+func PersistentFile(op *FileOptions) {
+	op.IsTemp = false
+}
diff --git a/credentialproviderpackage/pkg/filewriter/tmp_writer_test.go b/credentialproviderpackage/pkg/filewriter/tmp_writer_test.go
new file mode 100644
index 00000000..e5443749
--- /dev/null
+++ b/credentialproviderpackage/pkg/filewriter/tmp_writer_test.go
@@ -0,0 +1,159 @@
+package filewriter_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+
+	"credential-provider/pkg/filewriter"
+)
+
+func TestTmpWriterWriteValid(t *testing.T) {
+	folder := "tmp_folder"
+	folder2 := "tmp_folder_2"
+	err := os.MkdirAll(folder2, os.ModePerm)
+	if err != nil {
+		t.Fatalf("error setting up test: %v", err)
+	}
+	defer os.RemoveAll(folder)
+	defer os.RemoveAll(folder2)
+
+	tests := []struct {
+		testName string
+		dir      string
+		fileName string
+		content  []byte
+	}{
+		{
+			testName: "dir doesn't exist",
+			dir:      folder,
+			fileName: "TestTmpWriterWriteValid-success.yaml",
+			content: []byte(`
+			fake content
+			blablab
+			`),
+		},
+		{
+			testName: "dir exists",
+			dir:      folder2,
+			fileName: "test",
+			content: []byte(`
+			fake content
+			blablab
+			`),
+		},
+		{
+			testName: "empty file name",
+			dir:      folder,
+			fileName: "test",
+			content: []byte(`
+			fake content
+			blablab
+			`),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			tr, err := filewriter.NewWriter(tt.dir)
+			if err != nil {
+				t.Fatalf("failed creating tmpWriter error = %v", err)
+			}
+
+			gotPath, err := tr.Write(tt.fileName, tt.content)
+			if err != nil {
+				t.Fatalf("tmpWriter.Write() error = %v", err)
+			}
+
+			if !strings.HasPrefix(gotPath, tt.dir) {
+				t.Errorf("tmpWriter.Write() = %v, want to start with %v", gotPath, tt.dir)
+			}
+
+			if !strings.HasSuffix(gotPath, tt.fileName) {
+				t.Errorf("tmpWriter.Write() = %v, want to end with %v", gotPath, tt.fileName)
+			}
+
+			content, err := ioutil.ReadFile(gotPath)
+			if err != nil {
+				t.Fatalf("error reading written file: %v", err)
+			}
+
+			if string(content) != string(tt.content) {
+				t.Errorf("Write file content = %v, want %v", content, tt.content)
+			}
+		})
+	}
+}
+
+func TestTmpWriterWithDir(t *testing.T) {
+	rootFolder := "folder_root"
+	subFolder := "subFolder"
+	defer os.RemoveAll(rootFolder)
+
+	tr, err := filewriter.NewWriter(rootFolder)
+	if err != nil {
+		t.Fatalf("failed creating tmpWriter error = %v", err)
+	}
+
+	tr, err = tr.WithDir(subFolder)
+	if err != nil {
+		t.Fatalf("failed creating tmpWriter with subdir error = %v", err)
+	}
+
+	gotPath, err := tr.Write("file.txt", []byte("file content"))
+	if err != nil {
+		t.Fatalf("tmpWriter.Write() error = %v", err)
+	}
+
+	wantPathPrefix := filepath.Join(rootFolder, subFolder)
+	if !strings.HasPrefix(gotPath, wantPathPrefix) {
+		t.Errorf("tmpWriter.Write() = %v, want to start with %v", gotPath, wantPathPrefix)
+	}
+}
+
+func TestCreate(t *testing.T) {
+	dir := t.TempDir()
+	const fileName = "test.txt"
+
+	// Hard code the "generated". Its an implementation detail but we can't refactor it right now.
+	expectedPath := path.Join(dir, "generated", fileName)
+	expectedContent := []byte("test content")
+
+	fr, err := filewriter.NewWriter(dir)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	fh, path, err := fr.Create(fileName)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// We need to validate 2 things: (1) are the paths returned correct; (2) if we write content
+	// to the returned io.WriteCloser, is it written to the path also returened from the function.
+
+	if path != expectedPath {
+		t.Fatalf("Received: %v; Expected: %v", path, expectedPath)
+	}
+
+	if _, err := fh.Write(expectedContent); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := fh.Close(); err != nil {
+		t.Fatal(err)
+	}
+
+	content, err := os.ReadFile(expectedPath)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !cmp.Equal(content, expectedContent) {
+		t.Fatalf("Received: %v; Expected: %v", content, expectedContent)
+	}
+}
diff --git a/credentialproviderpackage/pkg/filewriter/writer.go b/credentialproviderpackage/pkg/filewriter/writer.go
new file mode 100644
index 00000000..fe6239da
--- /dev/null
+++ b/credentialproviderpackage/pkg/filewriter/writer.go
@@ -0,0 +1,99 @@
+package filewriter
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"io/fs"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+type writer struct {
+	dir     string
+	tempDir string
+}
+
+func NewWriter(dir string) (FileWriter, error) {
+	newFolder := filepath.Join(dir, DefaultTmpFolder)
+	if _, err := os.Stat(newFolder); errors.Is(err, os.ErrNotExist) {
+		err := os.MkdirAll(newFolder, os.ModePerm)
+		if err != nil {
+			return nil, fmt.Errorf("creating directory [%s]: %v", dir, err)
+		}
+	}
+	return &writer{dir: dir, tempDir: newFolder}, nil
+}
+
+func (w *writer) Write(fileName string, content []byte, opts ...FileOptionsFunc) (string, error) {
+	o := buildOptions(w, opts)
+
+	filePath := filepath.Join(o.BasePath, fileName)
+	err := ioutil.WriteFile(filePath, content, o.Permissions)
+	if err != nil {
+		return "", fmt.Errorf("writing to file [%s]: %v", filePath, err)
+	}
+
+	return filePath, nil
+}
+
+func (w *writer) WithDir(dir string) (FileWriter, error) {
+	return NewWriter(filepath.Join(w.dir, dir))
+}
+
+func (w *writer) Dir() string {
+	return w.dir
+}
+
+func (w *writer) TempDir() string {
+	return w.tempDir
+}
+
+func (w *writer) CleanUp() {
+	_, err := os.Stat(w.dir)
+	if err == nil {
+		os.RemoveAll(w.dir)
+	}
+}
+
+func (w *writer) CleanUpTemp() {
+	_, err := os.Stat(w.tempDir)
+	if err == nil {
+		os.RemoveAll(w.tempDir)
+	}
+}
+
+// Create creates a file with the given name rooted at w's base directory.
+func (w *writer) Create(name string, opts ...FileOptionsFunc) (_ io.WriteCloser, path string, _ error) {
+	o := buildOptions(w, opts)
+
+	path = filepath.Join(o.BasePath, name)
+	fh, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, o.Permissions)
+	return fh, path, err
+}
+
+type options struct {
+	BasePath    string
+	Permissions fs.FileMode
+}
+
+// buildOptions converts a set of FileOptionsFunc's to a single options struct.
+func buildOptions(w *writer, opts []FileOptionsFunc) options {
+	op := defaultFileOptions()
+	for _, fn := range opts {
+		fn(op)
+	}
+
+	var basePath string
+	if op.IsTemp {
+		basePath = w.tempDir
+	} else {
+		basePath = w.dir
+	}
+
+	return options{
+		BasePath:    basePath,
+		Permissions: op.Permissions,
+	}
+}
diff --git a/credentialproviderpackage/pkg/filewriter/writer_test.go b/credentialproviderpackage/pkg/filewriter/writer_test.go
new file mode 100644
index 00000000..1eb084d2
--- /dev/null
+++ b/credentialproviderpackage/pkg/filewriter/writer_test.go
@@ -0,0 +1,203 @@
+package filewriter_test
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"credential-provider/internal/test"
+	"credential-provider/pkg/filewriter"
+)
+
+func TestWriterWriteValid(t *testing.T) {
+	folder := "tmp_folder"
+	folder2 := "tmp_folder_2"
+	err := os.MkdirAll(folder2, os.ModePerm)
+	if err != nil {
+		t.Fatalf("error setting up test: %v", err)
+	}
+	defer os.RemoveAll(folder)
+	defer os.RemoveAll(folder2)
+
+	tests := []struct {
+		testName string
+		dir      string
+		fileName string
+		content  []byte
+	}{
+		{
+			testName: "test 1",
+			dir:      folder,
+			fileName: "TestWriterWriteValid-success.yaml",
+			content: []byte(`
+			fake content
+			blablab
+			`),
+		},
+		{
+			testName: "test 2",
+			dir:      folder2,
+			fileName: "TestWriterWriteValid-success.yaml",
+			content: []byte(`
+			fake content
+			blablab
+			`),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			tr, err := filewriter.NewWriter(tt.dir)
+			if err != nil {
+				t.Fatalf("failed creating writer error = %v", err)
+			}
+
+			gotPath, err := tr.Write(tt.fileName, tt.content)
+			if err != nil {
+				t.Fatalf("writer.Write() error = %v", err)
+			}
+
+			wantPath := filepath.Join(tt.dir, filewriter.DefaultTmpFolder, tt.fileName)
+			if strings.Compare(gotPath, wantPath) != 0 {
+				t.Errorf("writer.Write() = %v, want %v", gotPath, wantPath)
+			}
+
+			test.AssertFilesEquals(t, gotPath, wantPath)
+		})
+	}
+}
+
+func TestEmptyFileName(t *testing.T) {
+	folder := "tmp_folder"
+	defer os.RemoveAll(folder)
+	tr, err := filewriter.NewWriter(folder)
+	if err != nil {
+		t.Fatalf("failed creating writer error = %v", err)
+	}
+	_, err = tr.Write("", []byte("content"))
+	if err == nil {
+		t.Fatalf("writer.Write() error is nil")
+	}
+}
+
+func TestWriterWithDir(t *testing.T) {
+	rootFolder := "folder_root"
+	subFolder := "subFolder"
+	defer os.RemoveAll(rootFolder)
+
+	tr, err := filewriter.NewWriter(rootFolder)
+	if err != nil {
+		t.Fatalf("failed creating writer error = %v", err)
+	}
+
+	tr, err = tr.WithDir(subFolder)
+	if err != nil {
+		t.Fatalf("failed creating writer with subdir error = %v", err)
+	}
+
+	gotPath, err := tr.Write("file.txt", []byte("file content"))
+	if err != nil {
+		t.Fatalf("writer.Write() error = %v", err)
+	}
+
+	wantPathPrefix := filepath.Join(rootFolder, subFolder)
+	if !strings.HasPrefix(gotPath, wantPathPrefix) {
+		t.Errorf("writer.Write() = %v, want to start with %v", gotPath, wantPathPrefix)
+	}
+}
+
+func TestWriterWritePersistent(t *testing.T) {
+	folder := "tmp_folder_opt"
+	folder2 := "tmp_folder_2_opt"
+	err := os.MkdirAll(folder2, os.ModePerm)
+	if err != nil {
+		t.Fatalf("error setting up test: %v", err)
+	}
+	defer os.RemoveAll(folder)
+	defer os.RemoveAll(folder2)
+
+	tests := []struct {
+		testName string
+		dir      string
+		fileName string
+		content  []byte
+		options  []filewriter.FileOptionsFunc
+	}{
+		{
+			testName: "Write persistent file",
+			dir:      folder,
+			fileName: "TestWriterWriteValid-success.yaml",
+			content: []byte(`
+			fake content
+			blablab
+			`),
+			options: []filewriter.FileOptionsFunc{filewriter.PersistentFile},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			tr, err := filewriter.NewWriter(tt.dir)
+			if err != nil {
+				t.Fatalf("failed creating writer error = %v", err)
+			}
+
+			gotPath, err := tr.Write(tt.fileName, tt.content, tt.options...)
+			if err != nil {
+				t.Fatalf("writer.Write() error = %v", err)
+			}
+
+			wantPath := filepath.Join(tt.dir, tt.fileName)
+			if strings.Compare(gotPath, wantPath) != 0 {
+				t.Errorf("writer.Write() = %v, want %v", gotPath, wantPath)
+			}
+
+			test.AssertFilesEquals(t, gotPath, wantPath)
+		})
+	}
+}
+
+func TestWriterDir(t *testing.T) {
+	rootFolder := "folder_root"
+	defer os.RemoveAll(rootFolder)
+
+	tr, err := filewriter.NewWriter(rootFolder)
+	if err != nil {
+		t.Fatalf("failed creating writer error = %v", err)
+	}
+
+	if strings.Compare(tr.Dir(), rootFolder) != 0 {
+		t.Errorf("writer.Dir() = %v, want %v", tr.Dir(), rootFolder)
+	}
+}
+
+func TestWriterTempDir(t *testing.T) {
+	rootFolder := "folder_root"
+	tempFolder := fmt.Sprintf("%s/generated", rootFolder)
+	defer os.RemoveAll(rootFolder)
+
+	tr, err := filewriter.NewWriter(rootFolder)
+	if err != nil {
+		t.Fatalf("failed creating writer error = %v", err)
+	}
+
+	if strings.Compare(tr.TempDir(), tempFolder) != 0 {
+		t.Errorf("writer.TempDir() = %v, want %v", tr.TempDir(), tempFolder)
+	}
+}
+
+func TestWriterCleanUpTempDir(t *testing.T) {
+	rootFolder := "folder_root"
+	defer os.RemoveAll(rootFolder)
+
+	tr, err := filewriter.NewWriter(rootFolder)
+	if err != nil {
+		t.Fatalf("failed creating writer error = %v", err)
+	}
+
+	tr.CleanUpTemp()
+
+	if _, err := os.Stat(tr.TempDir()); err == nil {
+		t.Errorf("writer.CleanUp(), want err, got nil")
+	}
+}
diff --git a/credentialproviderpackage/pkg/templater/partialyaml.go b/credentialproviderpackage/pkg/templater/partialyaml.go
new file mode 100644
index 00000000..44112b27
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/partialyaml.go
@@ -0,0 +1,31 @@
+package templater
+
+import (
+	"reflect"
+	"strings"
+
+	"sigs.k8s.io/yaml"
+)
+
+type PartialYaml map[string]interface{}
+
+func (p PartialYaml) AddIfNotZero(k string, v interface{}) {
+	if !isZeroVal(v) {
+		p[k] = v
+	}
+}
+
+func isZeroVal(x interface{}) bool {
+	return x == nil || reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
+}
+
+func (p PartialYaml) ToYaml() (string, error) {
+	b, err := yaml.Marshal(p)
+	if err != nil {
+		return "", err
+	}
+	s := string(b)
+	s = strings.TrimSuffix(s, "\n")
+
+	return s, nil
+}
diff --git a/credentialproviderpackage/pkg/templater/partialyaml_test.go b/credentialproviderpackage/pkg/templater/partialyaml_test.go
new file mode 100644
index 00000000..464ed87e
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/partialyaml_test.go
@@ -0,0 +1,131 @@
+package templater_test
+
+import (
+	"reflect"
+	"testing"
+
+	"credential-provider/internal/test"
+	"credential-provider/pkg/templater"
+)
+
+func TestPartialYamlAddIfNotZero(t *testing.T) {
+	tests := []struct {
+		testName  string
+		p         templater.PartialYaml
+		k         string
+		v         interface{}
+		wantAdded bool
+		wantV     interface{}
+	}{
+		{
+			testName:  "add string",
+			p:         templater.PartialYaml{},
+			k:         "key",
+			v:         "value",
+			wantAdded: true,
+			wantV:     "value",
+		},
+		{
+			testName:  "add nil",
+			p:         templater.PartialYaml{},
+			k:         "key",
+			v:         nil,
+			wantAdded: false,
+			wantV:     nil,
+		},
+		{
+			testName:  "add empty string",
+			p:         templater.PartialYaml{},
+			k:         "key",
+			v:         "",
+			wantAdded: false,
+			wantV:     nil,
+		},
+		{
+			testName: "add present string",
+			p: templater.PartialYaml{
+				"key": "value_old",
+			},
+			k:         "key",
+			v:         "value_new",
+			wantAdded: true,
+			wantV:     "value_new",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			tt.p.AddIfNotZero(tt.k, tt.v)
+
+			gotV, gotAdded := tt.p[tt.k]
+			if tt.wantAdded != gotAdded {
+				t.Errorf("PartialYaml.AddIfNotZero() wasAdded = %v, wantAdded %v", gotAdded, tt.wantAdded)
+			}
+
+			if !reflect.DeepEqual(gotV, tt.wantV) {
+				t.Errorf("PartialYaml.AddIfNotZero() gotValue = %v, wantValue %v", gotV, tt.wantV)
+			}
+		})
+	}
+}
+
+func TestPartialYamlToYaml(t *testing.T) {
+	tests := []struct {
+		testName string
+		p        templater.PartialYaml
+		wantFile string
+		wantErr  bool
+	}{
+		{
+			testName: "simple object",
+			p: templater.PartialYaml{
+				"key1": "value 1",
+				"key2": 2,
+				"key3": "value3",
+			},
+			wantFile: "testdata/partial_yaml_object_expected.yaml",
+			wantErr:  false,
+		},
+		{
+			testName: "map",
+			p: templater.PartialYaml{
+				"key1": "value 1",
+				"key2": 2,
+				"key3": map[string]string{
+					"key_nest1": "value nest",
+					"key_nest2": "value nest 2",
+				},
+				"key4": map[string]interface{}{
+					"key_nest1": "value nest",
+					"key_nest2": 22,
+				},
+			},
+			wantFile: "testdata/partial_yaml_map_expected.yaml",
+			wantErr:  false,
+		},
+		{
+			testName: "array",
+			p: templater.PartialYaml{
+				"key1": "value 1",
+				"key2": 2,
+				"key3": []string{"value array 1", "value array 2"},
+				"key4": []interface{}{
+					map[string]interface{}{
+						"key_in_nest_array":   "value",
+						"key_in_nest_array_2": 22,
+					},
+				},
+			},
+			wantFile: "testdata/partial_yaml_array_expected.yaml",
+			wantErr:  false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			got, err := tt.p.ToYaml()
+			if (err != nil) != tt.wantErr {
+				t.Fatalf("PartialYaml.ToYaml() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			test.AssertContentToFile(t, got, tt.wantFile)
+		})
+	}
+}
diff --git a/credentialproviderpackage/pkg/templater/templater.go b/credentialproviderpackage/pkg/templater/templater.go
new file mode 100644
index 00000000..437adb63
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/templater.go
@@ -0,0 +1,66 @@
+package templater
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"text/template"
+
+	"credential-provider/pkg/filewriter"
+)
+
+type Templater struct {
+	writer filewriter.FileWriter
+}
+
+func New(writer filewriter.FileWriter) *Templater {
+	return &Templater{
+		writer: writer,
+	}
+}
+
+func (t *Templater) WriteToFile(templateContent string, data interface{}, fileName string, f ...filewriter.FileOptionsFunc) (filePath string, err error) {
+	bytes, err := Execute(templateContent, data)
+	if err != nil {
+		return "", err
+	}
+	writtenFilePath, err := t.writer.Write(fileName, bytes, f...)
+	if err != nil {
+		return "", fmt.Errorf("writing template file: %v", err)
+	}
+
+	return writtenFilePath, nil
+}
+
+func (t *Templater) WriteBytesToFile(content []byte, fileName string, f ...filewriter.FileOptionsFunc) (filePath string, err error) {
+	writtenFilePath, err := t.writer.Write(fileName, content, f...)
+	if err != nil {
+		return "", fmt.Errorf("writing template file: %v", err)
+	}
+
+	return writtenFilePath, nil
+}
+
+func Execute(templateContent string, data interface{}) ([]byte, error) {
+	temp := template.New("tmpl")
+	funcMap := map[string]interface{}{
+		"indent": func(spaces int, v string) string {
+			pad := strings.Repeat(" ", spaces)
+			return pad + strings.Replace(v, "\n", "\n"+pad, -1)
+		},
+		"stringsJoin": strings.Join,
+	}
+	temp = temp.Funcs(funcMap)
+
+	temp, err := temp.Parse(templateContent)
+	if err != nil {
+		return nil, fmt.Errorf("parsing template: %v", err)
+	}
+
+	var buf bytes.Buffer
+	err = temp.Execute(&buf, data)
+	if err != nil {
+		return nil, fmt.Errorf("substituting values for template: %v", err)
+	}
+	return buf.Bytes(), nil
+}
diff --git a/credentialproviderpackage/pkg/templater/templater_test.go b/credentialproviderpackage/pkg/templater/templater_test.go
new file mode 100644
index 00000000..f6c74386
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/templater_test.go
@@ -0,0 +1,143 @@
+package templater_test
+
+import (
+	"os"
+	"strings"
+	"testing"
+
+	"credential-provider/internal/test"
+	"credential-provider/pkg/filewriter"
+	"credential-provider/pkg/templater"
+)
+
+func TestTemplaterWriteToFileSuccess(t *testing.T) {
+	type dataStruct struct {
+		Key1, Key2, Key3, KeyAndValue3 string
+		Conditional                    bool
+	}
+
+	tests := []struct {
+		testName     string
+		templateFile string
+		data         dataStruct
+		fileName     string
+		wantFilePath string
+		wantErr      bool
+	}{
+		{
+			testName:     "with conditional true",
+			templateFile: "testdata/test1_template.yaml",
+			data: dataStruct{
+				Key1:        "value_1",
+				Key2:        "value_2",
+				Key3:        "value_3",
+				Conditional: true,
+			},
+			fileName:     "file_tmp.yaml",
+			wantFilePath: "testdata/test1_conditional_true_want.yaml",
+			wantErr:      false,
+		},
+		{
+			testName:     "with conditional false",
+			templateFile: "testdata/test1_template.yaml",
+			data: dataStruct{
+				Key1:        "value_1",
+				Key2:        "value_2",
+				Key3:        "value_3",
+				Conditional: false,
+			},
+			fileName:     "file_tmp.yaml",
+			wantFilePath: "testdata/test1_conditional_false_want.yaml",
+			wantErr:      false,
+		},
+		{
+			testName:     "with indent",
+			templateFile: "testdata/test_indent_template.yaml",
+			data: dataStruct{
+				Key1:         "value_1",
+				Key2:         "value_2",
+				KeyAndValue3: "key3: value_3",
+				Conditional:  true,
+			},
+			fileName:     "file_tmp.yaml",
+			wantFilePath: "testdata/test_indent_want.yaml",
+			wantErr:      false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			_, writer := test.NewWriter(t)
+			tr := templater.New(writer)
+			templateContent := test.ReadFile(t, tt.templateFile)
+			gotFilePath, err := tr.WriteToFile(templateContent, tt.data, tt.fileName)
+			if (err != nil) != tt.wantErr {
+				t.Fatalf("Templater.WriteToFile() error = %v, wantErr %v", err, tt.wantErr)
+			}
+
+			if !strings.HasSuffix(gotFilePath, tt.fileName) {
+				t.Errorf("Templater.WriteToFile()  = %v, want to end with %v", gotFilePath, tt.fileName)
+			}
+
+			test.AssertFilesEquals(t, gotFilePath, tt.wantFilePath)
+		})
+	}
+}
+
+func TestTemplaterWriteToFileError(t *testing.T) {
+	folder := "tmp_folder"
+	defer os.RemoveAll(folder)
+
+	writer, err := filewriter.NewWriter(folder)
+	if err != nil {
+		t.Fatalf("failed creating writer error = #{err}")
+	}
+
+	type dataStruct struct {
+		Key1, Key2, Key3 string
+		Conditional      bool
+	}
+
+	tests := []struct {
+		testName     string
+		templateFile string
+		data         dataStruct
+		fileName     string
+	}{
+		{
+			testName:     "invalid template",
+			templateFile: "testdata/invalid_template.yaml",
+			data: dataStruct{
+				Key1:        "value_1",
+				Key2:        "value_2",
+				Key3:        "value_3",
+				Conditional: true,
+			},
+			fileName: "file_tmp.yaml",
+		},
+		{
+			testName:     "data doesn't exist",
+			templateFile: "testdata/key4_template.yaml",
+			data: dataStruct{
+				Key1:        "value_1",
+				Key2:        "value_2",
+				Key3:        "value_3",
+				Conditional: false,
+			},
+			fileName: "file_tmp.yaml",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.testName, func(t *testing.T) {
+			tr := templater.New(writer)
+			templateContent := test.ReadFile(t, tt.templateFile)
+			gotFilePath, err := tr.WriteToFile(templateContent, tt.data, tt.fileName)
+			if err == nil {
+				t.Errorf("Templater.WriteToFile() error = nil")
+			}
+
+			if gotFilePath != "" {
+				t.Errorf("Templater.WriteToFile() = %v, want nil", gotFilePath)
+			}
+		})
+	}
+}
diff --git a/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml b/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml
new file mode 100644
index 00000000..5dc5cbfc
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml
@@ -0,0 +1,5 @@
+key1: {{ .Key1 }}
+key2: {{ .Key2 
+{{ if .Conditional }}
+key3: {{ .Key3 }}
+{{ end }}
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml b/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml
new file mode 100644
index 00000000..c65df82d
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml
@@ -0,0 +1,6 @@
+key1: {{ .Key1 }}
+key2: {{ .Key2 }}
+{{ if .Conditional }}
+key3: {{ .Key3 }}
+{{ end }}
+key4: {{ .Key4 }}
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml
new file mode 100644
index 00000000..d492ba49
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml
@@ -0,0 +1,8 @@
+key1: value 1
+key2: 2
+key3:
+- value array 1
+- value array 2
+key4:
+- key_in_nest_array: value
+  key_in_nest_array_2: 22
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml
new file mode 100644
index 00000000..37c88490
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml
@@ -0,0 +1,8 @@
+key1: value 1
+key2: 2
+key3:
+  key_nest1: value nest
+  key_nest2: value nest 2
+key4:
+  key_nest1: value nest
+  key_nest2: 22
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml
new file mode 100644
index 00000000..6edb56eb
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml
@@ -0,0 +1,3 @@
+key1: value 1
+key2: 2
+key3: value3
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml
new file mode 100644
index 00000000..d6154a35
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml
@@ -0,0 +1,2 @@
+key1: value_1
+key2: value_2
diff --git a/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml
new file mode 100644
index 00000000..f1cfcb93
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml
@@ -0,0 +1,4 @@
+key1: value_1
+key2: value_2
+
+key3: value_3
diff --git a/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml b/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml
new file mode 100644
index 00000000..113d3ca7
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml
@@ -0,0 +1,5 @@
+key1: {{ .Key1 }}
+key2: {{ .Key2 }}
+{{ if .Conditional }}
+key3: {{ .Key3 }}
+{{ end }}
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml b/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml
new file mode 100644
index 00000000..4393f80b
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml
@@ -0,0 +1,5 @@
+key1: {{ .Key1 }}
+key2: {{ .Key2 }}
+{{ if .Conditional }}
+{{ .KeyAndValue3 | indent 2 }}
+{{ end }}
\ No newline at end of file
diff --git a/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml b/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml
new file mode 100644
index 00000000..6d4ebdbf
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml
@@ -0,0 +1,4 @@
+key1: value_1
+key2: value_2
+
+  key3: value_3
diff --git a/credentialproviderpackage/pkg/templater/yaml.go b/credentialproviderpackage/pkg/templater/yaml.go
new file mode 100644
index 00000000..42e2ee7f
--- /dev/null
+++ b/credentialproviderpackage/pkg/templater/yaml.go
@@ -0,0 +1,39 @@
+package templater
+
+import (
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/yaml"
+)
+
+const objectSeparator string = "\n---\n"
+
+func AppendYamlResources(resources ...[]byte) []byte {
+	separator := []byte(objectSeparator)
+
+	size := 0
+	for _, resource := range resources {
+		size += len(resource) + len(separator)
+	}
+
+	b := make([]byte, 0, size)
+	for _, resource := range resources {
+		b = append(b, resource...)
+		b = append(b, separator...)
+	}
+
+	return b
+}
+
+func ObjectsToYaml(objs ...runtime.Object) ([]byte, error) {
+	r := [][]byte{}
+	for _, o := range objs {
+		b, err := yaml.Marshal(o)
+		if err != nil {
+			return nil, fmt.Errorf("failed to marshal object: %v", err)
+		}
+		r = append(r, b)
+	}
+	return AppendYamlResources(r...), nil
+}
diff --git a/credentialproviderpackage/pkg/utils/utils.go b/credentialproviderpackage/pkg/utils/utils.go
new file mode 100644
index 00000000..125e4b55
--- /dev/null
+++ b/credentialproviderpackage/pkg/utils/utils.go
@@ -0,0 +1,18 @@
+package utils
+
+import (
+	"log"
+	"os"
+)
+
+var (
+	InfoLogger    *log.Logger
+	WarningLogger *log.Logger
+	ErrorLogger   *log.Logger
+)
+
+func init() {
+	InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
+	WarningLogger = log.New(os.Stderr, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
+	ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
+}
diff --git a/credentialproviderpackage/skaffold.yaml b/credentialproviderpackage/skaffold.yaml
new file mode 100644
index 00000000..ee11c0d6
--- /dev/null
+++ b/credentialproviderpackage/skaffold.yaml
@@ -0,0 +1,24 @@
+apiVersion: skaffold/v3
+kind: Config
+metadata:
+  name: credential-provider
+build:
+  tagPolicy:
+    envTemplate:
+      template: "{{.EMPTY}}"
+  artifacts:
+  - image: credentialpackage
+    docker:
+      dockerfile: Dockerfile
+manifests:
+  helm:
+    releases:
+    - name: credential-provider-helm
+      chartPath: charts/credential-provider-package
+      setValueTemplates:
+        image.registry: "{{.ECR_PUBLIC_REGISTRY}}"
+        image.repository: "credentialpackage"
+        image.tag: "{{.IMAGE_DIGEST_credentialpackage}}"
+        image.digest: "{{.IMAGE_DIGEST_credentialpackage}}"
+        sourceRegistry: "{{.SKAFFOLD_DEFAULT_REPO}}"
+

From 084346c2fafde134f1a14452537317a7d2284b7d Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Wed, 22 Feb 2023 14:59:19 +0000
Subject: [PATCH 02/16] Adding new line to end of Dockerfile and Makefile

---
 credentialproviderpackage/Dockerfile | 2 +-
 credentialproviderpackage/Makefile   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/credentialproviderpackage/Dockerfile b/credentialproviderpackage/Dockerfile
index 89262061..4660509b 100644
--- a/credentialproviderpackage/Dockerfile
+++ b/credentialproviderpackage/Dockerfile
@@ -11,4 +11,4 @@ COPY pkg/ pkg/
 ARG SKAFFOLD_GO_GCFLAGS
 RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o app cmd/aws-credential-provider/*.go
 
-CMD ["/app/app"]
\ No newline at end of file
+CMD ["/app/app"]
diff --git a/credentialproviderpackage/Makefile b/credentialproviderpackage/Makefile
index 81b94688..a87e28d2 100644
--- a/credentialproviderpackage/Makefile
+++ b/credentialproviderpackage/Makefile
@@ -38,4 +38,4 @@ run:
 	$(GO) run .
 
 test: build
-	$(GO) test ./... `$(GO) list $(GOTESTS) | grep -v mocks | grep -v fake | grep -v testutil` -coverprofile cover.out
\ No newline at end of file
+	$(GO) test ./... `$(GO) list $(GOTESTS) | grep -v mocks | grep -v fake | grep -v testutil` -coverprofile cover.out

From c6b3a91a49dcfdaf4ff7e800cbd4c411fe08e1f9 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Thu, 23 Feb 2023 00:34:16 +0000
Subject: [PATCH 03/16] Changed matchImages to take array instead of one
 repository. Change Initialize interface to remove socketpath and add that to
 bottlerocket constructor, Removed Notes from chart, moved from Deployment to
 Daemonset

---
 .../templates/NOTES.txt                       |  1 -
 .../{deployment.yaml => daemonset.yaml}       |  8 +++-
 .../credential-provider-package/values.yaml   |  9 ++--
 .../cmd/aws-credential-provider/main.go       | 22 +++++-----
 .../configurator/bottlerocket/bottlerocket.go | 23 +++++-----
 .../bottlerocket/bottlerocket_test.go         | 42 +++++++++++++------
 .../pkg/configurator/configurator.go          |  2 +-
 .../pkg/configurator/linux/linux.go           |  2 +-
 .../pkg/configurator/linux/linux_test.go      | 42 +++++++++++++------
 .../templates/credential-provider-config.yaml |  4 +-
 .../expected-config-multiple-patterns.yaml    | 18 ++++++++
 .../pkg/constants/constants.go                |  8 ++--
 12 files changed, 119 insertions(+), 62 deletions(-)
 delete mode 100644 credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt
 rename credentialproviderpackage/charts/credential-provider-package/templates/{deployment.yaml => daemonset.yaml} (90%)
 create mode 100644 credentialproviderpackage/pkg/configurator/linux/testdata/expected-config-multiple-patterns.yaml

diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt b/credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt
deleted file mode 100644
index f9339199..00000000
--- a/credentialproviderpackage/charts/credential-provider-package/templates/NOTES.txt
+++ /dev/null
@@ -1 +0,0 @@
-1. Installed Credential Provider Package
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
similarity index 90%
rename from credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml
rename to credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
index bc98d2fa..cf082464 100644
--- a/credentialproviderpackage/charts/credential-provider-package/templates/deployment.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
@@ -1,5 +1,5 @@
 apiVersion: apps/v1
-kind: Deployment
+kind: DaemonSet
 metadata:
   name: {{ include "credential-provider.fullname" . }}
   namespace: {{ .Release.Namespace | default .Values.defaultNamespace | quote }}
@@ -49,10 +49,14 @@ spec:
           env:
             - name: OS_TYPE
               value: {{ template "template.getOSName" }}
+            - name: MATCH_IMAGES
+              value: '{{ join "," .Values.application.matchImages }}'
+            - name: DEFAULT_CACHE_DURATION
+              value: {{.Values.application.defaultCacheDuration}}
       volumes:
         - name: aws-creds
           secret:
-            secretName: {{.Values.secretName}}
+            secretName: {{.Values.application.secretName}}
             optional: false
         {{- if eq $os "bottlerocket" }}
         - name: socket
diff --git a/credentialproviderpackage/charts/credential-provider-package/values.yaml b/credentialproviderpackage/charts/credential-provider-package/values.yaml
index 78edfce9..ee2de9da 100644
--- a/credentialproviderpackage/charts/credential-provider-package/values.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/values.yaml
@@ -9,13 +9,14 @@ defaultNamespace: eksa-packages
 image:
   repository: "credential-provider-package"
   tag: "{{credential-provider-package-tag}}"
-  digest: sha256:320e4bdbe1cf0ccc4a594c84acc339cf4d652c40d84b5313f79ae68b9493db85
+  digest: "{{credential-provider-package-digest}}"
   pullPolicy: IfNotPresent
 
 # application values
-secretName: aws-cred
-imagePatterns: ""
-defaultCacheDuration: ""
+application:
+  secretName: aws-cred
+  matchImages: []
+  defaultCacheDuration: ""
 
 imagePullSecrets: []
 nameOverride: ""
diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
index 2545ddb4..a8c62331 100644
--- a/credentialproviderpackage/cmd/aws-credential-provider/main.go
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -20,7 +20,7 @@ import (
 func checkErrAndLog(err error, logger *log.Logger) {
 	if err != nil {
 		logger.Println(err)
-		os.Exit(0)
+		os.Exit(1)
 	}
 }
 
@@ -31,24 +31,24 @@ func main() {
 	osType := strings.ToLower(os.Getenv("OS_TYPE"))
 	if osType == "" {
 		utils.ErrorLogger.Println("Missing Environment Variable OS")
-		os.Exit(0)
+		os.Exit(1)
 	}
 	config := createCredentialProviderConfigOptions()
 	if osType == constants.BottleRocket {
 		socket, err := os.Stat(constants.SocketPath)
 		checkErrAndLog(err, utils.ErrorLogger)
 		if socket.Mode().Type() == fs.ModeSocket {
-			configurator = bottlerocket.NewBottleRocketConfigurator()
-			configurator.Initialize(constants.SocketPath, config)
+			configurator = bottlerocket.NewBottleRocketConfigurator(constants.SocketPath)
+
 		} else {
 			utils.ErrorLogger.Printf("Unexpected type %s expected socket\n", socket.Mode().Type())
-			os.Exit(0)
+			os.Exit(1)
 		}
 	} else {
 		configurator = linux.NewLinuxConfigurator()
-		configurator.Initialize("", config)
 	}
 
+	configurator.Initialize(config)
 	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, constants.Profile)
 	checkErrAndLog(err, utils.ErrorLogger)
 	utils.InfoLogger.Println("Aws credentials configured")
@@ -103,13 +103,15 @@ func main() {
 }
 
 func createCredentialProviderConfigOptions() constants.CredentialProviderConfigOptions {
-	imagePatterns := os.Getenv("IMAGE_PATTERNS")
-	if imagePatterns == "" {
-		imagePatterns = constants.ImagePattern
+	imagePatternsValues := os.Getenv("MATCH_IMAGES")
+	if imagePatternsValues == "" {
+		imagePatternsValues = constants.DefaultImagePattern
 	}
+	imagePatterns := strings.Split(imagePatternsValues, ",")
+
 	defaultCacheDuration := os.Getenv("DEFAULT_CACHE_DURATION")
 	if defaultCacheDuration == "" {
-		defaultCacheDuration = constants.CacheDuration
+		defaultCacheDuration = constants.DefaultCacheDuration
 	}
 
 	return constants.CredentialProviderConfigOptions{
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
index d39c0fb4..aac462db 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
@@ -46,20 +46,21 @@ type kubernetes struct {
 
 var _ configurator.Configurator = (*bottleRocket)(nil)
 
-func NewBottleRocketConfigurator() *bottleRocket {
-	return &bottleRocket{}
+func NewBottleRocketConfigurator(socketPath string) *bottleRocket {
+	return &bottleRocket{
+		client: http.Client{
+			Transport: &http.Transport{
+				DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
+					return net.Dial("unix", socketPath)
+				},
+			},
+		},
+	}
 }
 
-func (b *bottleRocket) Initialize(socketPath string, config constants.CredentialProviderConfigOptions) {
+func (b *bottleRocket) Initialize(config constants.CredentialProviderConfigOptions) {
 	b.baseURL = "http://localhost/"
 	b.config = config
-	b.client = http.Client{
-		Transport: &http.Transport{
-			DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
-				return net.Dial("unix", socketPath)
-			},
-		},
-	}
 }
 
 func (b *bottleRocket) UpdateAWSCredentials(path string, profile string) error {
@@ -156,7 +157,7 @@ func createCredentialProviderPayload(config constants.CredentialProviderConfigOp
 			credentialProviders{
 				ecrCredentialProvider{
 					Enabled:       true,
-					ImagePatterns: []string{config.ImagePatterns},
+					ImagePatterns: config.ImagePatterns,
 					CacheDuration: config.DefaultCacheDuration,
 				},
 			},
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
index f76e9511..52572089 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
@@ -38,8 +38,8 @@ func Test_bottleRocket_CommitChanges(t *testing.T) {
 			fields: fields{
 				client: http.Client{},
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			wantErr: false,
@@ -53,8 +53,8 @@ func Test_bottleRocket_CommitChanges(t *testing.T) {
 			fields: fields{
 				client: http.Client{},
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			wantErr: true,
@@ -209,8 +209,8 @@ func Test_bottleRocket_UpdateCredentialProvider(t *testing.T) {
 			fields: fields{
 				client: http.Client{},
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			patchResponse: response{
@@ -225,7 +225,7 @@ func Test_bottleRocket_UpdateCredentialProvider(t *testing.T) {
 			fields: fields{
 				client: http.Client{},
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        "123456789.dkr.ecr.test-region.amazonaws.com",
+					ImagePatterns:        []string{"123456789.dkr.ecr.test-region.amazonaws.com"},
 					DefaultCacheDuration: "24h",
 				},
 			},
@@ -236,13 +236,29 @@ func Test_bottleRocket_UpdateCredentialProvider(t *testing.T) {
 			},
 			wantErr: false,
 		},
+		{
+			name: "multiple match images for credential provider",
+			fields: fields{
+				client: http.Client{},
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns:        []string{"123456789.dkr.ecr.test-region.amazonaws.com", "987654321.dkr.ecr.test-region.amazonaws.com"},
+					DefaultCacheDuration: "24h",
+				},
+			},
+			patchResponse: response{
+				statusCode:   http.StatusNoContent,
+				expectedBody: []byte("{\"kubernetes\":{\"credential-providers\":{\"ecr-credential-provider\":{\"cache-duration\":\"24h\",\"enabled\":true,\"image-patterns\":[\"123456789.dkr.ecr.test-region.amazonaws.com\",\"987654321.dkr.ecr.test-region.amazonaws.com\"]}}}}"),
+				responseMsg:  "",
+			},
+			wantErr: false,
+		},
 		{
 			name: "failed credential provider update",
 			fields: fields{
 				client: http.Client{},
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			patchResponse: response{
@@ -305,16 +321,16 @@ func Test_bottleRocket_Initialize(t *testing.T) {
 			args: args{
 				socketPath: "/test/path.sock",
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			b := NewBottleRocketConfigurator()
-			b.Initialize(tt.args.socketPath, tt.args.config)
+			b := NewBottleRocketConfigurator(tt.args.socketPath)
+			b.Initialize(tt.args.config)
 			assert.Equal(t, tt.baseUrl, b.baseURL)
 			assert.Equal(t, tt.args.config, b.config)
 			assert.NotNil(t, b.client)
diff --git a/credentialproviderpackage/pkg/configurator/configurator.go b/credentialproviderpackage/pkg/configurator/configurator.go
index 04f160bf..cd28a06b 100644
--- a/credentialproviderpackage/pkg/configurator/configurator.go
+++ b/credentialproviderpackage/pkg/configurator/configurator.go
@@ -4,7 +4,7 @@ import "credential-provider/pkg/constants"
 
 type Configurator interface {
 	// Initialize Handles node specific configuration depending on OS
-	Initialize(filepath string, config constants.CredentialProviderConfigOptions)
+	Initialize(config constants.CredentialProviderConfigOptions)
 
 	// UpdateAWSCredentials Handles AWS Credential Setup
 	UpdateAWSCredentials(sourcePath string, profile string) error
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
index 9fef9088..6b4478bc 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -37,7 +37,7 @@ func NewLinuxConfigurator() *linuxOS {
 	}
 }
 
-func (c *linuxOS) Initialize(_ string, config constants.CredentialProviderConfigOptions) {
+func (c *linuxOS) Initialize(config constants.CredentialProviderConfigOptions) {
 	c.config = config
 }
 
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux_test.go b/credentialproviderpackage/pkg/configurator/linux/linux_test.go
index 6430b012..17d05eed 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux_test.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux_test.go
@@ -39,8 +39,8 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				extraArgsPath: dir,
 				basePath:      dir,
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			args:             args{line: ""},
@@ -49,6 +49,24 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 			want: fmt.Sprintf(" --feature-gates=KubeletCredentialProviders=true "+
 				"--image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
 		},
+		{
+			name: "test multiple match patterns",
+			fields: fields{
+				profile:       "eksa-packages",
+				extraArgsPath: dir,
+				basePath:      dir,
+				config: constants.CredentialProviderConfigOptions{
+					ImagePatterns: []string{"1234567.dkr.ecr.us-east-1.amazonaws.com",
+						"7654321.dkr.ecr.us-west-2.amazonaws.com"},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
+				},
+			},
+			args:             args{line: ""},
+			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			configWantPath:   "testdata/expected-config-multiple-patterns.yaml",
+			want: fmt.Sprintf(" --feature-gates=KubeletCredentialProviders=true "+
+				"--image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
+		},
 		{
 			name: "skip credential provider if already provided",
 			fields: fields{
@@ -56,8 +74,8 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				extraArgsPath: dir,
 				basePath:      dir,
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			args:             args{line: " --feature-gates=KubeletCredentialProviders=true"},
@@ -72,8 +90,8 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				extraArgsPath: dir,
 				basePath:      dir,
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			args:             args{line: " --feature-gates=KubeletCredentialProviders=false --image-credential-provider-config=blah"},
@@ -127,8 +145,8 @@ func Test_linuxOS_UpdateAWSCredentials(t *testing.T) {
 				extraArgsPath: dir,
 				basePath:      dir,
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 			args: args{
@@ -183,7 +201,6 @@ func Test_linuxOS_Initialize(t *testing.T) {
 		config        constants.CredentialProviderConfigOptions
 	}
 	type args struct {
-		in0    string
 		config constants.CredentialProviderConfigOptions
 	}
 	tests := []struct {
@@ -194,10 +211,9 @@ func Test_linuxOS_Initialize(t *testing.T) {
 		{
 			name: "simple initialization",
 			args: args{
-				in0: "",
 				config: constants.CredentialProviderConfigOptions{
-					ImagePatterns:        constants.ImagePattern,
-					DefaultCacheDuration: constants.CacheDuration,
+					ImagePatterns:        []string{constants.DefaultImagePattern},
+					DefaultCacheDuration: constants.DefaultCacheDuration,
 				},
 			},
 		},
@@ -205,7 +221,7 @@ func Test_linuxOS_Initialize(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			c := NewLinuxConfigurator()
-			c.Initialize(tt.args.in0, tt.args.config)
+			c.Initialize(tt.args.config)
 			assert.Equal(t, c.config, tt.args.config)
 		})
 	}
diff --git a/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml b/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml
index 4701c6f2..3dc29279 100644
--- a/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml
+++ b/credentialproviderpackage/pkg/configurator/linux/templates/credential-provider-config.yaml
@@ -2,8 +2,8 @@ apiVersion: kubelet.config.k8s.io/v1alpha1
 kind: CredentialProviderConfig
 providers:
   - name: ecr-credential-provider
-    matchImages:
-      - "{{.imagePattern}}"
+    matchImages:{{range $val := .imagePattern}}
+      - "{{$val}}"{{end}}
     defaultCacheDuration: "{{.cacheDuration}}"
     apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
     env:
diff --git a/credentialproviderpackage/pkg/configurator/linux/testdata/expected-config-multiple-patterns.yaml b/credentialproviderpackage/pkg/configurator/linux/testdata/expected-config-multiple-patterns.yaml
new file mode 100644
index 00000000..41f9a587
--- /dev/null
+++ b/credentialproviderpackage/pkg/configurator/linux/testdata/expected-config-multiple-patterns.yaml
@@ -0,0 +1,18 @@
+apiVersion: kubelet.config.k8s.io/v1alpha1
+kind: CredentialProviderConfig
+providers:
+  - name: ecr-credential-provider
+    matchImages:
+      - "1234567.dkr.ecr.us-east-1.amazonaws.com"
+      - "7654321.dkr.ecr.us-west-2.amazonaws.com"
+    defaultCacheDuration: "30m"
+    apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
+    env:
+      - name: AWS_PROFILE
+        value: eksa-packages
+      - name: AWS_CONFIG_FILE
+        value: /eksa-packages/aws-creds
+      - name: PATH
+        value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/eksa-packages
+      - name: HOME
+        value: /eksa-packages/
diff --git a/credentialproviderpackage/pkg/constants/constants.go b/credentialproviderpackage/pkg/constants/constants.go
index ec272ad5..b665859d 100644
--- a/credentialproviderpackage/pkg/constants/constants.go
+++ b/credentialproviderpackage/pkg/constants/constants.go
@@ -2,9 +2,9 @@ package constants
 
 const (
 	// Credential Provider constants
-	ImagePattern     = "*.dkr.ecr.*.amazonaws.com"
-	CacheDuration    = "30m"
-	CredProviderFile = "credential-provider-config.yaml"
+	DefaultImagePattern  = "*.dkr.ecr.*.amazonaws.com"
+	DefaultCacheDuration = "30m"
+	CredProviderFile     = "credential-provider-config.yaml"
 
 	// Aws Credentials
 	CredSrcPath   = "/secrets/aws-creds/config"
@@ -36,6 +36,6 @@ const (
 )
 
 type CredentialProviderConfigOptions struct {
-	ImagePatterns        string
+	ImagePatterns        []string
 	DefaultCacheDuration string
 }

From 4011c007253e65119d165f55531cd4365407ac77 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Thu, 23 Feb 2023 13:42:57 +0000
Subject: [PATCH 04/16] Namespacing serviceaccount.yaml and removing comments
 from values.yaml

---
 .../templates/serviceaccount.yaml               |  1 +
 .../credential-provider-package/values.yaml     | 17 -----------------
 2 files changed, 1 insertion(+), 17 deletions(-)

diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml
index 65ffbfdb..9d261be5 100644
--- a/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/serviceaccount.yaml
@@ -3,6 +3,7 @@ apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: {{ include "credential-provider.serviceAccountName" . }}
+  namespace: {{ .Release.Namespace | default .Values.defaultNamespace | quote }}
   labels:
     {{- include "credential-provider.labels" . | nindent 4 }}
   {{- with .Values.serviceAccount.annotations }}
diff --git a/credentialproviderpackage/charts/credential-provider-package/values.yaml b/credentialproviderpackage/charts/credential-provider-package/values.yaml
index ee2de9da..2184f8a3 100644
--- a/credentialproviderpackage/charts/credential-provider-package/values.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/values.yaml
@@ -34,27 +34,10 @@ serviceAccount:
 podAnnotations: {}
 
 podSecurityContext: {}
-  # fsGroup: 2000
 
 securityContext: {}
-  # capabilities:
-  #   drop:
-  #   - ALL
-  # readOnlyRootFilesystem: true
-  # runAsNonRoot: true
-  # runAsUser: 1000
 
 resources: {}
-  # We usually recommend not to specify default resources and to leave this as a conscious
-  # choice for the user. This also increases chances charts run on environments with little
-  # resources, such as Minikube. If you do want to specify resources, uncomment the following
-  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
-  # limits:
-  #   cpu: 100m
-  #   memory: 128Mi
-  # requests:
-  #   cpu: 100m
-  #   memory: 128Mi
 
 nodeSelector: {}
 

From 32cfebdff98a20ac82f5778b9ad38a134d0a06c4 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Thu, 23 Feb 2023 15:47:10 +0000
Subject: [PATCH 05/16] Copy binaries always to cover update case. Update
 references to docker to Amazon Linux 2

---
 .../credential-provider-package/templates/_helpers.tpl    | 2 +-
 .../credential-provider-package/templates/daemonset.yaml  | 2 +-
 credentialproviderpackage/pkg/configurator/linux/linux.go | 8 ++++----
 credentialproviderpackage/pkg/constants/constants.go      | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl b/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl
index eb3e20b8..629f9696 100644
--- a/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/_helpers.tpl
@@ -80,7 +80,7 @@ Function to figure out os name
 {{- if contains "Bottlerocket" .status.nodeInfo.osImage -}}
 {{- printf "bottlerocket" -}}
 {{- else if contains "Amazon Linux" .status.nodeInfo.osImage -}}
-{{- printf "docker" -}}
+{{- printf "amazonlinux" -}}
 {{- else -}}
 {{- printf "other" -}}
 {{- end }}
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
index cf082464..0547a089 100644
--- a/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
@@ -62,7 +62,7 @@ spec:
         - name: socket
           hostPath:
             path: /run/api.sock
-        {{- else if eq $os "docker"}}
+        {{- else if eq $os "amazonlinux"}}
         - name: kubelet-extra-args
           hostPath:
             path: /etc/default/kubelet
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
index 6b4478bc..6d388ce2 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -211,11 +211,11 @@ func (c *linuxOS) updateKubeletArguments(line string) string {
 		}
 		args += val
 
+		val, err = copyBinaries()
+		if err != nil {
+			utils.ErrorLogger.Printf("Error coping binaries %v\n", err)
+		}
 		if !strings.Contains(line, "image-credential-provider-bin-dir") {
-			val, err = copyBinaries()
-			if err != nil {
-				utils.ErrorLogger.Printf("Error coping binaries %v\n", err)
-			}
 			args += val
 		}
 	}
diff --git a/credentialproviderpackage/pkg/constants/constants.go b/credentialproviderpackage/pkg/constants/constants.go
index b665859d..2a4205bb 100644
--- a/credentialproviderpackage/pkg/constants/constants.go
+++ b/credentialproviderpackage/pkg/constants/constants.go
@@ -29,7 +29,7 @@ const (
 type OSType string
 
 const (
-	Docker       OSType = "docker"
+	AmazonLinux  OSType = "amazonlinux"
 	Ubuntu              = "ubuntu"
 	Redhat              = "redhat"
 	BottleRocket        = "bottlerocket"

From 09764a30fb977f1b208e269d8ea95822fd408859 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Tue, 28 Feb 2023 14:16:47 +0000
Subject: [PATCH 06/16] Adding AWS Profile to be configurable

---
 .../templates/daemonset.yaml                           |  2 ++
 .../charts/credential-provider-package/values.yaml     |  1 +
 .../cmd/aws-credential-provider/main.go                | 10 +++++++---
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
index 0547a089..cd680fad 100644
--- a/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
@@ -49,6 +49,8 @@ spec:
           env:
             - name: OS_TYPE
               value: {{ template "template.getOSName" }}
+            - name: AWS_PROFILE
+              value: {{.Values.application.profile}}
             - name: MATCH_IMAGES
               value: '{{ join "," .Values.application.matchImages }}'
             - name: DEFAULT_CACHE_DURATION
diff --git a/credentialproviderpackage/charts/credential-provider-package/values.yaml b/credentialproviderpackage/charts/credential-provider-package/values.yaml
index 2184f8a3..b312b70b 100644
--- a/credentialproviderpackage/charts/credential-provider-package/values.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/values.yaml
@@ -17,6 +17,7 @@ application:
   secretName: aws-cred
   matchImages: []
   defaultCacheDuration: ""
+  profile: ""
 
 imagePullSecrets: []
 nameOverride: ""
diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
index a8c62331..bc4e361b 100644
--- a/credentialproviderpackage/cmd/aws-credential-provider/main.go
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -33,6 +33,10 @@ func main() {
 		utils.ErrorLogger.Println("Missing Environment Variable OS")
 		os.Exit(1)
 	}
+	profile := os.Getenv("AWS_PROFILE")
+	if profile == "" {
+		profile = constants.Profile
+	}
 	config := createCredentialProviderConfigOptions()
 	if osType == constants.BottleRocket {
 		socket, err := os.Stat(constants.SocketPath)
@@ -49,11 +53,11 @@ func main() {
 	}
 
 	configurator.Initialize(config)
-	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, constants.Profile)
+	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
 	checkErrAndLog(err, utils.ErrorLogger)
 	utils.InfoLogger.Println("Aws credentials configured")
 
-	err = configurator.UpdateCredentialProvider(constants.Profile)
+	err = configurator.UpdateCredentialProvider(profile)
 	checkErrAndLog(err, utils.ErrorLogger)
 	utils.InfoLogger.Println("Credential Provider Configured")
 
@@ -79,7 +83,7 @@ func main() {
 				}
 				if event.Has(fsnotify.Create) {
 					if event.Name == constants.CredWatchData {
-						err = configurator.UpdateAWSCredentials(constants.CredSrcPath, constants.Profile)
+						err = configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
 						checkErrAndLog(err, utils.ErrorLogger)
 						utils.InfoLogger.Println("Aws credentials successfully changed")
 					}

From 20cf973c0e7224a9342cf39480591b2031f0eb44 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Fri, 3 Mar 2023 18:42:32 +0000
Subject: [PATCH 07/16] Update go version to 1.19, cleanup chart, cleanup
 bottlerocket tests

---
 credentialproviderpackage/Dockerfile          |  2 +-
 .../credential-provider-package/Chart.yaml    | 23 ++------------
 .../templates/daemonset.yaml                  |  2 +-
 .../cmd/aws-credential-provider/main.go       | 31 ++++++++++---------
 credentialproviderpackage/go.mod              |  2 +-
 .../configurator/bottlerocket/bottlerocket.go |  4 +--
 .../bottlerocket/bottlerocket_test.go         | 21 ++++++++++---
 7 files changed, 42 insertions(+), 43 deletions(-)

diff --git a/credentialproviderpackage/Dockerfile b/credentialproviderpackage/Dockerfile
index 4660509b..6cadc0b1 100644
--- a/credentialproviderpackage/Dockerfile
+++ b/credentialproviderpackage/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.18-buster
+FROM golang:1.19-buster
 ENV GOTRACEBACK=single
 ENV GOPROXY=direct
 WORKDIR /app
diff --git a/credentialproviderpackage/charts/credential-provider-package/Chart.yaml b/credentialproviderpackage/charts/credential-provider-package/Chart.yaml
index 499d9bf3..45fe512c 100644
--- a/credentialproviderpackage/charts/credential-provider-package/Chart.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/Chart.yaml
@@ -1,24 +1,7 @@
 apiVersion: v2
 name: credential-provider-package
-description: A Helm chart for Kubernetes
-
-# A chart can be either an 'application' or a 'library' chart.
-#
-# Application charts are a collection of templates that can be packaged into versioned archives
-# to be deployed.
-#
-# Library charts provide useful utilities or functions for the chart developer. They're included as
-# a dependency of application charts to inject those utilities and functions into the rendering
-# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+description: A Helm chart for credential-provider-package, an application for configuring credentials via Kubelet Credential Provider
 type: application
-
-# This is the chart version. This version number should be incremented each time you make changes
-# to the chart and its templates, including the app version.
-# Versions are expected to follow Semantic Versioning (https://semver.org/)
 version: 0.1.0
-
-# This is the version number of the application being deployed. This version number should be
-# incremented each time you make changes to the application. Versions are not expected to
-# follow Semantic Versioning. They should reflect the version the application is using.
-# It is recommended to use it with quotes.
-appVersion: "1.16.0"
+sources:
+  - https://github.com/aws/eks-anywhere-packages/credentialproviderpackage
diff --git a/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
index cd680fad..d4d49420 100644
--- a/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/templates/daemonset.yaml
@@ -48,7 +48,7 @@ spec:
             {{- end}}
           env:
             - name: OS_TYPE
-              value: {{ template "template.getOSName" }}
+              value: {{ $os }}
             - name: AWS_PROFILE
               value: {{.Values.application.profile}}
             - name: MATCH_IMAGES
diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
index bc4e361b..97c8dc47 100644
--- a/credentialproviderpackage/cmd/aws-credential-provider/main.go
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -2,13 +2,11 @@ package main
 
 import (
 	_ "embed"
+	"github.com/fsnotify/fsnotify"
 	"io/fs"
 	"log"
 	"os"
 	"strings"
-	"time"
-
-	"github.com/fsnotify/fsnotify"
 
 	cfg "credential-provider/pkg/configurator"
 	"credential-provider/pkg/configurator/bottlerocket"
@@ -19,18 +17,16 @@ import (
 
 func checkErrAndLog(err error, logger *log.Logger) {
 	if err != nil {
-		logger.Println(err)
+		logger.Fatal(err)
 		os.Exit(1)
 	}
 }
 
 func main() {
-	utils.InfoLogger.Println("Running at " + time.Now().UTC().String())
-
 	var configurator cfg.Configurator
 	osType := strings.ToLower(os.Getenv("OS_TYPE"))
 	if osType == "" {
-		utils.ErrorLogger.Println("Missing Environment Variable OS")
+		utils.ErrorLogger.Println("Missing Environment Variable OS_TYPE")
 		os.Exit(1)
 	}
 	profile := os.Getenv("AWS_PROFILE")
@@ -40,13 +36,14 @@ func main() {
 	config := createCredentialProviderConfigOptions()
 	if osType == constants.BottleRocket {
 		socket, err := os.Stat(constants.SocketPath)
-		checkErrAndLog(err, utils.ErrorLogger)
+		if err != nil {
+			utils.ErrorLogger.Fatal(err)
+		}
 		if socket.Mode().Type() == fs.ModeSocket {
 			configurator = bottlerocket.NewBottleRocketConfigurator(constants.SocketPath)
 
 		} else {
-			utils.ErrorLogger.Printf("Unexpected type %s expected socket\n", socket.Mode().Type())
-			os.Exit(1)
+			utils.ErrorLogger.Fatalf("Unexpected type %s expected socket\n", socket.Mode().Type())
 		}
 	} else {
 		configurator = linux.NewLinuxConfigurator()
@@ -54,22 +51,28 @@ func main() {
 
 	configurator.Initialize(config)
 	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
-	checkErrAndLog(err, utils.ErrorLogger)
+	if err != nil {
+		utils.ErrorLogger.Fatal(err)
+	}
 	utils.InfoLogger.Println("Aws credentials configured")
 
 	err = configurator.UpdateCredentialProvider(profile)
-	checkErrAndLog(err, utils.ErrorLogger)
+	if err != nil {
+		utils.ErrorLogger.Fatal(err)
+	}
 	utils.InfoLogger.Println("Credential Provider Configured")
 
 	err = configurator.CommitChanges()
-	checkErrAndLog(err, utils.ErrorLogger)
+	if err != nil {
+		utils.ErrorLogger.Fatal(err)
+	}
 
 	utils.InfoLogger.Println("Kubelet Restarted")
 
 	// Creating watcher for credentials
 	watcher, err := fsnotify.NewWatcher()
 	if err != nil {
-		log.Fatal(err)
+		utils.ErrorLogger.Fatal(err)
 	}
 	defer watcher.Close()
 
diff --git a/credentialproviderpackage/go.mod b/credentialproviderpackage/go.mod
index 98f1d575..069ffee7 100644
--- a/credentialproviderpackage/go.mod
+++ b/credentialproviderpackage/go.mod
@@ -1,6 +1,6 @@
 module credential-provider
 
-go 1.18
+go 1.19
 
 require (
 	github.com/fsnotify/fsnotify v1.6.0
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
index aac462db..d3d6cc15 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
@@ -20,7 +20,7 @@ type bottleRocket struct {
 	config  constants.CredentialProviderConfigOptions
 }
 
-type AwsCred struct {
+type awsCred struct {
 	Aws Aws `json:"aws"`
 }
 type Aws struct {
@@ -142,7 +142,7 @@ func createCredentialsPayload(content string, profile string) ([]byte, error) {
 		Profile: profile,
 	}
 
-	creds := AwsCred{Aws: aws}
+	creds := awsCred{Aws: aws}
 
 	payload, err := json.Marshal(creds)
 	if err != nil {
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
index 52572089..ee9a5308 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
@@ -2,10 +2,13 @@ package bottlerocket
 
 import (
 	"bytes"
+	"encoding/base64"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
+	"os"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -83,6 +86,17 @@ func Test_bottleRocket_CommitChanges(t *testing.T) {
 }
 
 func Test_bottleRocket_UpdateAWSCredentials(t *testing.T) {
+	file, err := os.Open("testdata/testcreds")
+	if err != nil {
+		t.Errorf("Failed to open testcreds")
+	}
+	content, err := io.ReadAll(file)
+	if err != nil {
+		t.Errorf("Failed to read testcreds")
+	}
+	encodedSecret := base64.StdEncoding.EncodeToString(content)
+	expectedBody := fmt.Sprintf("{\"aws\":{\"config\":\"%s\",\"profile\":\"eksa-packages\",\"region\":\"\"}}", encodedSecret)
+
 	type fields struct {
 		client  http.Client
 		baseURL string
@@ -92,7 +106,6 @@ func Test_bottleRocket_UpdateAWSCredentials(t *testing.T) {
 		path    string
 		profile string
 	}
-
 	tests := []struct {
 		name           string
 		fields         fields
@@ -113,7 +126,7 @@ func Test_bottleRocket_UpdateAWSCredentials(t *testing.T) {
 			},
 			patchResponse: response{
 				statusCode:   http.StatusNoContent,
-				expectedBody: []byte("{\"aws\":{\"config\":\"W3Byb2ZpbGUgZWtzYS1wYWNrYWdlc10KYXdzX2FjY2Vzc19rZXlfaWQ9QUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5PXdKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVk=\",\"profile\":\"eksa-packages\",\"region\":\"\"}}"),
+				expectedBody: []byte(expectedBody),
 				responseMsg:  "",
 			},
 			commitResponse: response{
@@ -134,7 +147,7 @@ func Test_bottleRocket_UpdateAWSCredentials(t *testing.T) {
 			},
 			patchResponse: response{
 				statusCode:   http.StatusNoContent,
-				expectedBody: []byte("{\"aws\":{\"config\":\"W3Byb2ZpbGUgZWtzYS1wYWNrYWdlc10KYXdzX2FjY2Vzc19rZXlfaWQ9QUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5PXdKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVk=\",\"profile\":\"eksa-packages\",\"region\":\"\"}}"),
+				expectedBody: []byte(expectedBody),
 				responseMsg:  "",
 			},
 			commitResponse: response{
@@ -155,7 +168,7 @@ func Test_bottleRocket_UpdateAWSCredentials(t *testing.T) {
 			},
 			patchResponse: response{
 				statusCode:   http.StatusNotFound,
-				expectedBody: []byte("{\"aws\":{\"config\":\"W3Byb2ZpbGUgZWtzYS1wYWNrYWdlc10KYXdzX2FjY2Vzc19rZXlfaWQ9QUtJQUlPU0ZPRE5ON0VYQU1QTEUKYXdzX3NlY3JldF9hY2Nlc3Nfa2V5PXdKYWxyWFV0bkZFTUkvSzdNREVORy9iUHhSZmlDWUVYQU1QTEVLRVk=\",\"profile\":\"eksa-packages\",\"region\":\"\"}}"),
+				expectedBody: []byte(expectedBody),
 				responseMsg:  "",
 			},
 			commitResponse: response{

From e0fc178f286780bed5574b7d9bc9364c41139b0a Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Fri, 3 Mar 2023 19:02:26 +0000
Subject: [PATCH 08/16] Move util to pkg/log. Update to go 1.19 in makefile

---
 credentialproviderpackage/Makefile            |  2 +-
 .../cmd/aws-credential-provider/main.go       | 40 ++++++++-----------
 .../pkg/configurator/linux/linux.go           |  6 +--
 credentialproviderpackage/pkg/log/log.go      | 18 +++++++++
 credentialproviderpackage/pkg/utils/utils.go  | 18 ---------
 5 files changed, 39 insertions(+), 45 deletions(-)
 create mode 100644 credentialproviderpackage/pkg/log/log.go
 delete mode 100644 credentialproviderpackage/pkg/utils/utils.go

diff --git a/credentialproviderpackage/Makefile b/credentialproviderpackage/Makefile
index a87e28d2..f445d40d 100644
--- a/credentialproviderpackage/Makefile
+++ b/credentialproviderpackage/Makefile
@@ -2,7 +2,7 @@ SHELL = /usr/bin/env bash -o pipefail
 .SHELLFLAGS = -ec
 
 REPO_ROOT=$(shell git rev-parse --show-toplevel)/credentialproviderpackage
-GOLANG_VERSION?="1.18"
+GOLANG_VERSION?="1.19"
 #GO ?= $(shell source $(REPO_ROOT)/scripts/common.sh && build::common::get_go_path $(GOLANG_VERSION))/go
 GO = go
 
diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
index 97c8dc47..8db9590f 100644
--- a/credentialproviderpackage/cmd/aws-credential-provider/main.go
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -4,7 +4,6 @@ import (
 	_ "embed"
 	"github.com/fsnotify/fsnotify"
 	"io/fs"
-	"log"
 	"os"
 	"strings"
 
@@ -12,21 +11,14 @@ import (
 	"credential-provider/pkg/configurator/bottlerocket"
 	"credential-provider/pkg/configurator/linux"
 	"credential-provider/pkg/constants"
-	"credential-provider/pkg/utils"
+	"credential-provider/pkg/log"
 )
 
-func checkErrAndLog(err error, logger *log.Logger) {
-	if err != nil {
-		logger.Fatal(err)
-		os.Exit(1)
-	}
-}
-
 func main() {
 	var configurator cfg.Configurator
 	osType := strings.ToLower(os.Getenv("OS_TYPE"))
 	if osType == "" {
-		utils.ErrorLogger.Println("Missing Environment Variable OS_TYPE")
+		log.ErrorLogger.Println("Missing Environment Variable OS_TYPE")
 		os.Exit(1)
 	}
 	profile := os.Getenv("AWS_PROFILE")
@@ -37,13 +29,13 @@ func main() {
 	if osType == constants.BottleRocket {
 		socket, err := os.Stat(constants.SocketPath)
 		if err != nil {
-			utils.ErrorLogger.Fatal(err)
+			log.ErrorLogger.Fatal(err)
 		}
 		if socket.Mode().Type() == fs.ModeSocket {
 			configurator = bottlerocket.NewBottleRocketConfigurator(constants.SocketPath)
 
 		} else {
-			utils.ErrorLogger.Fatalf("Unexpected type %s expected socket\n", socket.Mode().Type())
+			log.ErrorLogger.Fatalf("Unexpected type %s expected socket\n", socket.Mode().Type())
 		}
 	} else {
 		configurator = linux.NewLinuxConfigurator()
@@ -52,27 +44,27 @@ func main() {
 	configurator.Initialize(config)
 	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
 	if err != nil {
-		utils.ErrorLogger.Fatal(err)
+		log.ErrorLogger.Fatal(err)
 	}
-	utils.InfoLogger.Println("Aws credentials configured")
+	log.InfoLogger.Println("Aws credentials configured")
 
 	err = configurator.UpdateCredentialProvider(profile)
 	if err != nil {
-		utils.ErrorLogger.Fatal(err)
+		log.ErrorLogger.Fatal(err)
 	}
-	utils.InfoLogger.Println("Credential Provider Configured")
+	log.InfoLogger.Println("Credential Provider Configured")
 
 	err = configurator.CommitChanges()
 	if err != nil {
-		utils.ErrorLogger.Fatal(err)
+		log.ErrorLogger.Fatal(err)
 	}
 
-	utils.InfoLogger.Println("Kubelet Restarted")
+	log.InfoLogger.Println("Kubelet Restarted")
 
 	// Creating watcher for credentials
 	watcher, err := fsnotify.NewWatcher()
 	if err != nil {
-		utils.ErrorLogger.Fatal(err)
+		log.ErrorLogger.Fatal(err)
 	}
 	defer watcher.Close()
 
@@ -87,22 +79,24 @@ func main() {
 				if event.Has(fsnotify.Create) {
 					if event.Name == constants.CredWatchData {
 						err = configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
-						checkErrAndLog(err, utils.ErrorLogger)
-						utils.InfoLogger.Println("Aws credentials successfully changed")
+						if err != nil {
+							log.ErrorLogger.Fatal(err)
+						}
+						log.InfoLogger.Println("Aws credentials successfully changed")
 					}
 				}
 			case err, ok := <-watcher.Errors:
 				if !ok {
 					return
 				}
-				log.Println("error:", err)
+				log.WarningLogger.Printf("filewatcher error: %v", err)
 			}
 		}
 	}()
 
 	err = watcher.Add(constants.CredWatchPath)
 	if err != nil {
-		log.Fatal(err)
+		log.ErrorLogger.Fatal(err)
 	}
 
 	// Block main goroutine forever.
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
index 6d388ce2..113b20f6 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -13,8 +13,8 @@ import (
 
 	"credential-provider/pkg/configurator"
 	"credential-provider/pkg/constants"
+	"credential-provider/pkg/log"
 	"credential-provider/pkg/templater"
-	"credential-provider/pkg/utils"
 )
 
 //go:embed templates/credential-provider-config.yaml
@@ -207,13 +207,13 @@ func (c *linuxOS) updateKubeletArguments(line string) string {
 	if !strings.Contains(line, "image-credential-provider-config") {
 		val, err := c.createConfig()
 		if err != nil {
-			utils.ErrorLogger.Printf("Error creating configuration %v", err)
+			log.ErrorLogger.Printf("Error creating configuration %v", err)
 		}
 		args += val
 
 		val, err = copyBinaries()
 		if err != nil {
-			utils.ErrorLogger.Printf("Error coping binaries %v\n", err)
+			log.ErrorLogger.Printf("Error coping binaries %v\n", err)
 		}
 		if !strings.Contains(line, "image-credential-provider-bin-dir") {
 			args += val
diff --git a/credentialproviderpackage/pkg/log/log.go b/credentialproviderpackage/pkg/log/log.go
new file mode 100644
index 00000000..b110586f
--- /dev/null
+++ b/credentialproviderpackage/pkg/log/log.go
@@ -0,0 +1,18 @@
+package log
+
+import (
+	"log"
+	"os"
+)
+
+var (
+	InfoLogger    *log.Logger
+	WarningLogger *log.Logger
+	ErrorLogger   *log.Logger
+)
+
+func init() {
+	InfoLogger = log.New(os.Stdout, "INFO: ", log.Ltime|log.Lshortfile)
+	WarningLogger = log.New(os.Stderr, "WARNING: ", log.Ltime|log.Lshortfile)
+	ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ltime|log.Lshortfile)
+}
diff --git a/credentialproviderpackage/pkg/utils/utils.go b/credentialproviderpackage/pkg/utils/utils.go
deleted file mode 100644
index 125e4b55..00000000
--- a/credentialproviderpackage/pkg/utils/utils.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package utils
-
-import (
-	"log"
-	"os"
-)
-
-var (
-	InfoLogger    *log.Logger
-	WarningLogger *log.Logger
-	ErrorLogger   *log.Logger
-)
-
-func init() {
-	InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
-	WarningLogger = log.New(os.Stderr, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
-	ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
-}

From a82c2e1c7bf3a072eca0556e6b37da1234e9fd95 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Wed, 8 Mar 2023 14:57:59 +0000
Subject: [PATCH 09/16] Removing time from log and updating global reads to
 group reads

---
 .../pkg/configurator/linux/linux.go                    | 10 +++++-----
 credentialproviderpackage/pkg/log/log.go               |  6 +++---
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
index 113b20f6..383eec20 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -152,24 +152,24 @@ func copyWithPermissons(srcpath, dstpath string, permission os.FileMode) (err er
 func copyBinaries() (string, error) {
 	srcPath := constants.BinPath + constants.ECRCredProviderBinary
 	dstPath := constants.BasePath + constants.ECRCredProviderBinary
-	err := copyWithPermissons(srcPath, dstPath, 0744)
+	err := copyWithPermissons(srcPath, dstPath, 0700)
 	if err != nil {
 		return "", err
 	}
 
-	err = os.Chmod(dstPath, 0744)
+	err = os.Chmod(dstPath, 0700)
 	if err != nil {
 		return "", err
 	}
 
 	srcPath = constants.BinPath + constants.IAMRolesSigningBinary
 	dstPath = constants.BasePath + constants.IAMRolesSigningBinary
-	err = copyWithPermissons(srcPath, dstPath, 0744)
+	err = copyWithPermissons(srcPath, dstPath, 0700)
 	if err != nil {
 		return "", err
 	}
 
-	err = os.Chmod(dstPath, 0744)
+	err = os.Chmod(dstPath, 0700)
 	if err != nil {
 		return "", err
 	}
@@ -191,7 +191,7 @@ func (c *linuxOS) createConfig() (string, error) {
 	if err != nil {
 		return "", nil
 	}
-	err = ioutil.WriteFile(dstPath, bytes, 0644)
+	err = ioutil.WriteFile(dstPath, bytes, 0600)
 	if err != nil {
 		return "", err
 	}
diff --git a/credentialproviderpackage/pkg/log/log.go b/credentialproviderpackage/pkg/log/log.go
index b110586f..049453a2 100644
--- a/credentialproviderpackage/pkg/log/log.go
+++ b/credentialproviderpackage/pkg/log/log.go
@@ -12,7 +12,7 @@ var (
 )
 
 func init() {
-	InfoLogger = log.New(os.Stdout, "INFO: ", log.Ltime|log.Lshortfile)
-	WarningLogger = log.New(os.Stderr, "WARNING: ", log.Ltime|log.Lshortfile)
-	ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ltime|log.Lshortfile)
+	InfoLogger = log.New(os.Stdout, "INFO: ", log.Lshortfile)
+	WarningLogger = log.New(os.Stderr, "WARNING: ", log.Lshortfile)
+	ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Lshortfile)
 }

From 24ea5ad6db159c8afddfdd4627b130f64827533a Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Wed, 8 Mar 2023 15:45:24 +0000
Subject: [PATCH 10/16] Updating Gosec

---
 .github/workflows/gosec.yml | 2 +-
 Makefile                    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml
index 154758c1..0f620692 100644
--- a/.github/workflows/gosec.yml
+++ b/.github/workflows/gosec.yml
@@ -17,4 +17,4 @@ jobs:
       - name: Run Gosec Security Scanner
         uses: securego/gosec@master
         with:
-          args: --exclude-dir=kubetest-plugins --exclude-dir generatebundlefile --exclude-dir ecrtokenrefresher  ./...
+          args: --exclude-dir=kubetest-plugins --exclude-dir generatebundlefile --exclude-dir ecrtokenrefresher --exclude-dir credentialproviderpackage  ./...
diff --git a/Makefile b/Makefile
index b88dbb0a..bed4f9eb 100644
--- a/Makefile
+++ b/Makefile
@@ -82,7 +82,7 @@ vet: ## Run go vet against code.
 
 gosec: ## Run gosec against code.
 	$(GO) install github.com/securego/gosec/v2/cmd/gosec@latest
-	gosec --exclude-dir generatebundlefile --exclude-dir ecrtokenrefresher  ./...
+	gosec --exclude-dir generatebundlefile --exclude-dir ecrtokenrefresher --exclude-dir credentialproviderpackage  ./...
 
 SIGNED_ARTIFACTS = pkg/signature/testdata/packagebundle_minControllerVersion.yaml.signed pkg/signature/testdata/packagebundle_valid.yaml.signed pkg/signature/testdata/pod_valid.yaml.signed api/testdata/bundle_one.yaml.signed api/testdata/bundle_two.yaml.signed
 ENVTEST_ASSETS_DIR=$(shell pwd)/testbin

From e0e4d52c02421bdeccba0e21d5b4fedafd9743d6 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Wed, 8 Mar 2023 18:17:06 +0000
Subject: [PATCH 11/16] Update Makefile for go version

---
 credentialproviderpackage/Makefile | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/credentialproviderpackage/Makefile b/credentialproviderpackage/Makefile
index f445d40d..af77b2b5 100644
--- a/credentialproviderpackage/Makefile
+++ b/credentialproviderpackage/Makefile
@@ -1,10 +1,10 @@
 SHELL = /usr/bin/env bash -o pipefail
 .SHELLFLAGS = -ec
 
-REPO_ROOT=$(shell git rev-parse --show-toplevel)/credentialproviderpackage
+REPO_ROOT=$(shell git rev-parse --show-toplevel)
+PROJECT_ROOT=$(REPO_ROOT)/credentialproviderpackage
 GOLANG_VERSION?="1.19"
-#GO ?= $(shell source $(REPO_ROOT)/scripts/common.sh && build::common::get_go_path $(GOLANG_VERSION))/go
-GO = go
+GO ?= $(shell source $(REPO_ROOT)/scripts/common.sh && build::common::get_go_path $(GOLANG_VERSION))/go
 
 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
 ifeq (,$(shell go env GOBIN))
@@ -26,9 +26,9 @@ clean: ## Clean output directory, and the built binary
 ##@ Build
 
 build: ## Build Binary
-	mkdir -p $(REPO_ROOT)/bin
+	mkdir -p $(PROJECT_ROOT)/bin
 	$(GO) mod tidy -compat=$(GOLANG_VERSION)
-	$(GO) build -o $(REPO_ROOT)/bin/aws-credential-provider $(REPO_ROOT)/cmd/aws-credential-provider/*.go
+	$(GO) build -o $(PROJECT_ROOT)/bin/aws-credential-provider $(PROJECT_ROOT)/cmd/aws-credential-provider/*.go
 
 build-linux:
 	[ -d bin ] || mkdir bin

From 55c13a710110dbe4bc02aa8704738fb5fb4049b4 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Wed, 8 Mar 2023 20:38:30 +0000
Subject: [PATCH 12/16] Formatting fixes

---
 .../cmd/aws-credential-provider/main.go           |  3 ++-
 .../configurator/bottlerocket/testdata/testcreds  |  2 +-
 .../pkg/configurator/linux/linux.go               | 15 +++------------
 .../pkg/configurator/linux/testdata/testcreds     |  2 +-
 4 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
index 8db9590f..f2d1ef9f 100644
--- a/credentialproviderpackage/cmd/aws-credential-provider/main.go
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -2,11 +2,12 @@ package main
 
 import (
 	_ "embed"
-	"github.com/fsnotify/fsnotify"
 	"io/fs"
 	"os"
 	"strings"
 
+	"github.com/fsnotify/fsnotify"
+
 	cfg "credential-provider/pkg/configurator"
 	"credential-provider/pkg/configurator/bottlerocket"
 	"credential-provider/pkg/configurator/linux"
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds b/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds
index cec03763..cc4ea03f 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/testdata/testcreds
@@ -1,3 +1,3 @@
 [profile eksa-packages]
 aws_access_key_id=AKIAIOSFODNN7EXAMPLE
-aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
\ No newline at end of file
+aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
index 383eec20..60323961 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -79,10 +79,7 @@ func (c *linuxOS) UpdateCredentialProvider(_ string) error {
 
 	out := strings.Join(lines, "\n")
 	err = ioutil.WriteFile(c.extraArgsPath, []byte(out), 0644)
-	if err != nil {
-		return err
-	}
-	return nil
+	return err
 }
 
 func (c *linuxOS) CommitChanges() error {
@@ -91,18 +88,12 @@ func (c *linuxOS) CommitChanges() error {
 		return err
 	}
 	err = killProcess(process)
-	if err != nil {
-		return err
-	}
-	return nil
+	return err
 }
 
 func killProcess(process ps.Process) error {
 	err := syscall.Kill(process.Pid(), syscall.SIGHUP)
-	if err != nil {
-		return err
-	}
-	return nil
+	return err
 }
 
 func findKubeletProcess() (ps.Process, error) {
diff --git a/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds b/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds
index cec03763..cc4ea03f 100644
--- a/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds
+++ b/credentialproviderpackage/pkg/configurator/linux/testdata/testcreds
@@ -1,3 +1,3 @@
 [profile eksa-packages]
 aws_access_key_id=AKIAIOSFODNN7EXAMPLE
-aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
\ No newline at end of file
+aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

From 2aa00eeea4e437de7eada487aab40a9e36d018c6 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Thu, 9 Mar 2023 12:52:18 +0000
Subject: [PATCH 13/16] Allowed arm builds for linux

---
 credentialproviderpackage/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/credentialproviderpackage/Makefile b/credentialproviderpackage/Makefile
index af77b2b5..9475500a 100644
--- a/credentialproviderpackage/Makefile
+++ b/credentialproviderpackage/Makefile
@@ -32,7 +32,7 @@ build: ## Build Binary
 
 build-linux:
 	[ -d bin ] || mkdir bin
-	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(MAKE) build
+	env CGO_ENABLED=0 GOOS=linux $(MAKE) build
 
 run:
 	$(GO) run .

From a7e35d07a15882bb0522e91f053fb07991ab5a00 Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Thu, 9 Mar 2023 13:40:53 +0000
Subject: [PATCH 14/16] Moving constants to individual files. Moved BR socket
 logic to BR constructor itself.

---
 .../cmd/aws-credential-provider/main.go       | 33 ++++++++++--------
 .../configurator/bottlerocket/bottlerocket.go | 19 ++++++++---
 .../bottlerocket/bottlerocket_test.go         |  6 ++--
 .../pkg/configurator/linux/linux.go           | 34 +++++++++++++------
 .../pkg/configurator/linux/linux_test.go      | 16 ++++-----
 .../pkg/constants/constants.go                | 29 ----------------
 6 files changed, 66 insertions(+), 71 deletions(-)

diff --git a/credentialproviderpackage/cmd/aws-credential-provider/main.go b/credentialproviderpackage/cmd/aws-credential-provider/main.go
index f2d1ef9f..b1621f8a 100644
--- a/credentialproviderpackage/cmd/aws-credential-provider/main.go
+++ b/credentialproviderpackage/cmd/aws-credential-provider/main.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	_ "embed"
-	"io/fs"
 	"os"
 	"strings"
 
@@ -15,8 +14,20 @@ import (
 	"credential-provider/pkg/log"
 )
 
+const (
+	bottleRocket = "bottlerocket"
+	socketPath   = "/run/api.sock"
+
+	// Aws Credentials
+	credSrcPath   = "/secrets/aws-creds/config"
+	awsProfile    = "eksa-packages"
+	credWatchData = "/secrets/aws-creds/..data"
+	credWatchPath = "/secrets/aws-creds/"
+)
+
 func main() {
 	var configurator cfg.Configurator
+	var err error
 	osType := strings.ToLower(os.Getenv("OS_TYPE"))
 	if osType == "" {
 		log.ErrorLogger.Println("Missing Environment Variable OS_TYPE")
@@ -24,26 +35,20 @@ func main() {
 	}
 	profile := os.Getenv("AWS_PROFILE")
 	if profile == "" {
-		profile = constants.Profile
+		profile = awsProfile
 	}
 	config := createCredentialProviderConfigOptions()
-	if osType == constants.BottleRocket {
-		socket, err := os.Stat(constants.SocketPath)
+	if osType == bottleRocket {
+		configurator, err = bottlerocket.NewBottleRocketConfigurator(socketPath)
 		if err != nil {
 			log.ErrorLogger.Fatal(err)
 		}
-		if socket.Mode().Type() == fs.ModeSocket {
-			configurator = bottlerocket.NewBottleRocketConfigurator(constants.SocketPath)
-
-		} else {
-			log.ErrorLogger.Fatalf("Unexpected type %s expected socket\n", socket.Mode().Type())
-		}
 	} else {
 		configurator = linux.NewLinuxConfigurator()
 	}
 
 	configurator.Initialize(config)
-	err := configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
+	err = configurator.UpdateAWSCredentials(credSrcPath, profile)
 	if err != nil {
 		log.ErrorLogger.Fatal(err)
 	}
@@ -78,8 +83,8 @@ func main() {
 					return
 				}
 				if event.Has(fsnotify.Create) {
-					if event.Name == constants.CredWatchData {
-						err = configurator.UpdateAWSCredentials(constants.CredSrcPath, profile)
+					if event.Name == credWatchData {
+						err = configurator.UpdateAWSCredentials(credSrcPath, profile)
 						if err != nil {
 							log.ErrorLogger.Fatal(err)
 						}
@@ -95,7 +100,7 @@ func main() {
 		}
 	}()
 
-	err = watcher.Add(constants.CredWatchPath)
+	err = watcher.Add(credWatchPath)
 	if err != nil {
 		log.ErrorLogger.Fatal(err)
 	}
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
index d3d6cc15..93adb9e4 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket.go
@@ -6,9 +6,11 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
+	"io/fs"
 	"io/ioutil"
 	"net"
 	"net/http"
+	"os"
 
 	"credential-provider/pkg/configurator"
 	"credential-provider/pkg/constants"
@@ -21,9 +23,9 @@ type bottleRocket struct {
 }
 
 type awsCred struct {
-	Aws Aws `json:"aws"`
+	Aws aws `json:"aws"`
 }
-type Aws struct {
+type aws struct {
 	Config  string `json:"config"`
 	Profile string `json:"profile"`
 	Region  string `json:"region"`
@@ -46,7 +48,14 @@ type kubernetes struct {
 
 var _ configurator.Configurator = (*bottleRocket)(nil)
 
-func NewBottleRocketConfigurator(socketPath string) *bottleRocket {
+func NewBottleRocketConfigurator(socketPath string) (*bottleRocket, error) {
+	socket, err := os.Stat(socketPath)
+	if err != nil {
+		return nil, err
+	}
+	if socket.Mode().Type() != fs.ModeSocket {
+		return nil, fmt.Errorf("Unexpected type %s expected socket\n", socket.Mode().Type())
+	}
 	return &bottleRocket{
 		client: http.Client{
 			Transport: &http.Transport{
@@ -55,7 +64,7 @@ func NewBottleRocketConfigurator(socketPath string) *bottleRocket {
 				},
 			},
 		},
-	}
+	}, nil
 }
 
 func (b *bottleRocket) Initialize(config constants.CredentialProviderConfigOptions) {
@@ -137,7 +146,7 @@ func (b *bottleRocket) sendSettingsSetRequest(payload []byte) error {
 }
 
 func createCredentialsPayload(content string, profile string) ([]byte, error) {
-	aws := Aws{
+	aws := aws{
 		Config:  content,
 		Profile: profile,
 	}
diff --git a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
index ee9a5308..6490a06e 100644
--- a/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
+++ b/credentialproviderpackage/pkg/configurator/bottlerocket/bottlerocket_test.go
@@ -320,8 +320,7 @@ func validatePatchRequest(w http.ResponseWriter, r *http.Request, t *testing.T,
 
 func Test_bottleRocket_Initialize(t *testing.T) {
 	type args struct {
-		socketPath string
-		config     constants.CredentialProviderConfigOptions
+		config constants.CredentialProviderConfigOptions
 	}
 	tests := []struct {
 		name    string
@@ -332,7 +331,6 @@ func Test_bottleRocket_Initialize(t *testing.T) {
 			name:    "simple initialization",
 			baseUrl: "http://localhost/",
 			args: args{
-				socketPath: "/test/path.sock",
 				config: constants.CredentialProviderConfigOptions{
 					ImagePatterns:        []string{constants.DefaultImagePattern},
 					DefaultCacheDuration: constants.DefaultCacheDuration,
@@ -342,7 +340,7 @@ func Test_bottleRocket_Initialize(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			b := NewBottleRocketConfigurator(tt.args.socketPath)
+			b := &bottleRocket{}
 			b.Initialize(tt.args.config)
 			assert.Equal(t, tt.baseUrl, b.baseURL)
 			assert.Equal(t, tt.args.config, b.config)
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux.go b/credentialproviderpackage/pkg/configurator/linux/linux.go
index 60323961..a493927f 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux.go
@@ -20,6 +20,18 @@ import (
 //go:embed templates/credential-provider-config.yaml
 var credProviderTemplate string
 
+const (
+	binPath          = "/eksa-binaries/"
+	basePath         = "/eksa-packages/"
+	credOutFile      = "aws-creds"
+	mountedExtraArgs = "/node-files/kubelet-extra-args"
+	credProviderFile = "credential-provider-config.yaml"
+
+	// Binaries
+	ecrCredProviderBinary = "ecr-credential-provider"
+	iamRolesSigningBinary = "aws_signing_helper"
+)
+
 type linuxOS struct {
 	profile       string
 	extraArgsPath string
@@ -32,8 +44,8 @@ var _ configurator.Configurator = (*linuxOS)(nil)
 func NewLinuxConfigurator() *linuxOS {
 	return &linuxOS{
 		profile:       "",
-		extraArgsPath: constants.MountedExtraArgs,
-		basePath:      constants.BasePath,
+		extraArgsPath: mountedExtraArgs,
+		basePath:      basePath,
 	}
 }
 
@@ -43,7 +55,7 @@ func (c *linuxOS) Initialize(config constants.CredentialProviderConfigOptions) {
 
 func (c *linuxOS) UpdateAWSCredentials(sourcePath string, profile string) error {
 	c.profile = profile
-	dstPath := c.basePath + constants.CredOutFile
+	dstPath := c.basePath + credOutFile
 
 	err := copyWithPermissons(sourcePath, dstPath, 0600)
 	return err
@@ -141,8 +153,8 @@ func copyWithPermissons(srcpath, dstpath string, permission os.FileMode) (err er
 }
 
 func copyBinaries() (string, error) {
-	srcPath := constants.BinPath + constants.ECRCredProviderBinary
-	dstPath := constants.BasePath + constants.ECRCredProviderBinary
+	srcPath := binPath + ecrCredProviderBinary
+	dstPath := basePath + ecrCredProviderBinary
 	err := copyWithPermissons(srcPath, dstPath, 0700)
 	if err != nil {
 		return "", err
@@ -153,8 +165,8 @@ func copyBinaries() (string, error) {
 		return "", err
 	}
 
-	srcPath = constants.BinPath + constants.IAMRolesSigningBinary
-	dstPath = constants.BasePath + constants.IAMRolesSigningBinary
+	srcPath = binPath + iamRolesSigningBinary
+	dstPath = basePath + iamRolesSigningBinary
 	err = copyWithPermissons(srcPath, dstPath, 0700)
 	if err != nil {
 		return "", err
@@ -164,19 +176,19 @@ func copyBinaries() (string, error) {
 	if err != nil {
 		return "", err
 	}
-	return fmt.Sprintf(" --image-credential-provider-bin-dir=%s", constants.BasePath), nil
+	return fmt.Sprintf(" --image-credential-provider-bin-dir=%s", basePath), nil
 }
 
 func (c *linuxOS) createConfig() (string, error) {
 	values := map[string]interface{}{
 		"profile":       c.profile,
-		"config":        constants.BasePath + constants.CredOutFile,
-		"home":          constants.BasePath,
+		"config":        basePath + credOutFile,
+		"home":          basePath,
 		"imagePattern":  c.config.ImagePatterns,
 		"cacheDuration": c.config.DefaultCacheDuration,
 	}
 
-	dstPath := c.basePath + constants.CredProviderFile
+	dstPath := c.basePath + credProviderFile
 
 	bytes, err := templater.Execute(credProviderTemplate, values)
 	if err != nil {
diff --git a/credentialproviderpackage/pkg/configurator/linux/linux_test.go b/credentialproviderpackage/pkg/configurator/linux/linux_test.go
index 17d05eed..9ad90002 100644
--- a/credentialproviderpackage/pkg/configurator/linux/linux_test.go
+++ b/credentialproviderpackage/pkg/configurator/linux/linux_test.go
@@ -44,10 +44,10 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				},
 			},
 			args:             args{line: ""},
-			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			outputConfigPath: dir + "/" + credProviderFile,
 			configWantPath:   "testdata/expected-config.yaml",
 			want: fmt.Sprintf(" --feature-gates=KubeletCredentialProviders=true "+
-				"--image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
+				"--image-credential-provider-config=%s%s", dir, credProviderFile),
 		},
 		{
 			name: "test multiple match patterns",
@@ -62,10 +62,10 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				},
 			},
 			args:             args{line: ""},
-			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			outputConfigPath: dir + "/" + credProviderFile,
 			configWantPath:   "testdata/expected-config-multiple-patterns.yaml",
 			want: fmt.Sprintf(" --feature-gates=KubeletCredentialProviders=true "+
-				"--image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
+				"--image-credential-provider-config=%s%s", dir, credProviderFile),
 		},
 		{
 			name: "skip credential provider if already provided",
@@ -79,9 +79,9 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				},
 			},
 			args:             args{line: " --feature-gates=KubeletCredentialProviders=true"},
-			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			outputConfigPath: dir + "/" + credProviderFile,
 			configWantPath:   "testdata/expected-config.yaml",
-			want:             fmt.Sprintf(" --image-credential-provider-config=%s%s", dir, constants.CredProviderFile),
+			want:             fmt.Sprintf(" --image-credential-provider-config=%s%s", dir, credProviderFile),
 		},
 		{
 			name: "skip both cred provider and feature gate if provided",
@@ -95,7 +95,7 @@ func Test_linuxOS_updateKubeletArguments(t *testing.T) {
 				},
 			},
 			args:             args{line: " --feature-gates=KubeletCredentialProviders=false --image-credential-provider-config=blah"},
-			outputConfigPath: dir + "/" + constants.CredProviderFile,
+			outputConfigPath: dir + "/" + credProviderFile,
 			configWantPath:   "",
 			want:             "",
 		},
@@ -158,7 +158,7 @@ func Test_linuxOS_UpdateAWSCredentials(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			dstFile := tt.fields.basePath + constants.CredOutFile
+			dstFile := tt.fields.basePath + credOutFile
 			c := &linuxOS{
 				profile:       tt.fields.profile,
 				extraArgsPath: tt.fields.extraArgsPath,
diff --git a/credentialproviderpackage/pkg/constants/constants.go b/credentialproviderpackage/pkg/constants/constants.go
index 2a4205bb..41a04ea2 100644
--- a/credentialproviderpackage/pkg/constants/constants.go
+++ b/credentialproviderpackage/pkg/constants/constants.go
@@ -4,35 +4,6 @@ const (
 	// Credential Provider constants
 	DefaultImagePattern  = "*.dkr.ecr.*.amazonaws.com"
 	DefaultCacheDuration = "30m"
-	CredProviderFile     = "credential-provider-config.yaml"
-
-	// Aws Credentials
-	CredSrcPath   = "/secrets/aws-creds/config"
-	Profile       = "eksa-packages"
-	CredWatchData = "/secrets/aws-creds/..data"
-	CredWatchPath = "/secrets/aws-creds/"
-
-	// BottleRocket
-	SocketPath = "/run/api.sock"
-
-	// Linux
-	BinPath          = "/eksa-binaries/"
-	BasePath         = "/eksa-packages/"
-	CredOutFile      = "aws-creds"
-	MountedExtraArgs = "/node-files/kubelet-extra-args"
-
-	// Binaries
-	ECRCredProviderBinary = "ecr-credential-provider"
-	IAMRolesSigningBinary = "aws_signing_helper"
-)
-
-type OSType string
-
-const (
-	AmazonLinux  OSType = "amazonlinux"
-	Ubuntu              = "ubuntu"
-	Redhat              = "redhat"
-	BottleRocket        = "bottlerocket"
 )
 
 type CredentialProviderConfigOptions struct {

From 0b49d95af10be432e9e12f5e4916592a1d27e84c Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Mon, 13 Mar 2023 13:53:05 +0000
Subject: [PATCH 15/16] Removing unused replicas in values.yaml

---
 .../charts/credential-provider-package/values.yaml               | 1 -
 1 file changed, 1 deletion(-)

diff --git a/credentialproviderpackage/charts/credential-provider-package/values.yaml b/credentialproviderpackage/charts/credential-provider-package/values.yaml
index b312b70b..24745371 100644
--- a/credentialproviderpackage/charts/credential-provider-package/values.yaml
+++ b/credentialproviderpackage/charts/credential-provider-package/values.yaml
@@ -1,7 +1,6 @@
 # Default values for credential-provider.
 # This is a YAML-formatted file.
 
-replicaCount: 1
 # -- sourceRegistry for all container images in chart.
 sourceRegistry: public.ecr.aws/eks-anywhere
 defaultNamespace: eksa-packages

From 1147cb23919a9c02da166b58e94b5ac7ae3a4dad Mon Sep 17 00:00:00 2001
From: Jun Shun Zhang <junshun@amazon.com>
Date: Mon, 13 Mar 2023 19:19:48 +0000
Subject: [PATCH 16/16] Adding new lines to partialyaml and updated tests

---
 credentialproviderpackage/pkg/templater/partialyaml.go          | 2 --
 .../pkg/templater/testdata/invalid_template.yaml                | 2 +-
 .../pkg/templater/testdata/key4_template.yaml                   | 2 +-
 .../pkg/templater/testdata/partial_yaml_array_expected.yaml     | 2 +-
 .../pkg/templater/testdata/partial_yaml_map_expected.yaml       | 2 +-
 .../pkg/templater/testdata/partial_yaml_object_expected.yaml    | 2 +-
 .../pkg/templater/testdata/test1_conditional_false_want.yaml    | 1 +
 .../pkg/templater/testdata/test1_conditional_true_want.yaml     | 1 +
 .../pkg/templater/testdata/test1_template.yaml                  | 2 +-
 .../pkg/templater/testdata/test_indent_template.yaml            | 2 +-
 .../pkg/templater/testdata/test_indent_want.yaml                | 1 +
 11 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/credentialproviderpackage/pkg/templater/partialyaml.go b/credentialproviderpackage/pkg/templater/partialyaml.go
index 44112b27..053caf03 100644
--- a/credentialproviderpackage/pkg/templater/partialyaml.go
+++ b/credentialproviderpackage/pkg/templater/partialyaml.go
@@ -2,7 +2,6 @@ package templater
 
 import (
 	"reflect"
-	"strings"
 
 	"sigs.k8s.io/yaml"
 )
@@ -25,7 +24,6 @@ func (p PartialYaml) ToYaml() (string, error) {
 		return "", err
 	}
 	s := string(b)
-	s = strings.TrimSuffix(s, "\n")
 
 	return s, nil
 }
diff --git a/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml b/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml
index 5dc5cbfc..d4010323 100644
--- a/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/invalid_template.yaml
@@ -2,4 +2,4 @@ key1: {{ .Key1 }}
 key2: {{ .Key2 
 {{ if .Conditional }}
 key3: {{ .Key3 }}
-{{ end }}
\ No newline at end of file
+{{ end }}
diff --git a/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml b/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml
index c65df82d..1cb80cf9 100644
--- a/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/key4_template.yaml
@@ -3,4 +3,4 @@ key2: {{ .Key2 }}
 {{ if .Conditional }}
 key3: {{ .Key3 }}
 {{ end }}
-key4: {{ .Key4 }}
\ No newline at end of file
+key4: {{ .Key4 }}
diff --git a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml
index d492ba49..3d6ff659 100644
--- a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_array_expected.yaml
@@ -5,4 +5,4 @@ key3:
 - value array 2
 key4:
 - key_in_nest_array: value
-  key_in_nest_array_2: 22
\ No newline at end of file
+  key_in_nest_array_2: 22
diff --git a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml
index 37c88490..e8948147 100644
--- a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_map_expected.yaml
@@ -5,4 +5,4 @@ key3:
   key_nest2: value nest 2
 key4:
   key_nest1: value nest
-  key_nest2: 22
\ No newline at end of file
+  key_nest2: 22
diff --git a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml
index 6edb56eb..c3c4509a 100644
--- a/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/partial_yaml_object_expected.yaml
@@ -1,3 +1,3 @@
 key1: value 1
 key2: 2
-key3: value3
\ No newline at end of file
+key3: value3
diff --git a/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml
index d6154a35..cedb76a4 100644
--- a/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_false_want.yaml
@@ -1,2 +1,3 @@
 key1: value_1
 key2: value_2
+
diff --git a/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml
index f1cfcb93..7116c47d 100644
--- a/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/test1_conditional_true_want.yaml
@@ -2,3 +2,4 @@ key1: value_1
 key2: value_2
 
 key3: value_3
+
diff --git a/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml b/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml
index 113d3ca7..9e60bf61 100644
--- a/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/test1_template.yaml
@@ -2,4 +2,4 @@ key1: {{ .Key1 }}
 key2: {{ .Key2 }}
 {{ if .Conditional }}
 key3: {{ .Key3 }}
-{{ end }}
\ No newline at end of file
+{{ end }}
diff --git a/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml b/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml
index 4393f80b..d8bfae53 100644
--- a/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/test_indent_template.yaml
@@ -2,4 +2,4 @@ key1: {{ .Key1 }}
 key2: {{ .Key2 }}
 {{ if .Conditional }}
 {{ .KeyAndValue3 | indent 2 }}
-{{ end }}
\ No newline at end of file
+{{ end }}
diff --git a/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml b/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml
index 6d4ebdbf..94a0244c 100644
--- a/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml
+++ b/credentialproviderpackage/pkg/templater/testdata/test_indent_want.yaml
@@ -2,3 +2,4 @@ key1: value_1
 key2: value_2
 
   key3: value_3
+