Skip to content

Commit 2554060

Browse files
committed
Add support for copying pull secrets
1 parent 5d254ef commit 2554060

File tree

4 files changed

+105
-3
lines changed

4 files changed

+105
-3
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
sandbox-operator
1+
sandbox-operator
2+
*.exe

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ spec:
6363

6464
## Configuration
6565

66+
### Clients
67+
6668
The Sandbox operator can leverage different clients, depending upon how authenitcation is configured for your cluster.
6769

68-
### Azure
70+
#### Azure
6971

7072
If Azure credentials are provided to the operators environment, it will perform a lookup of each user in the `owners` field and fetch that users `ObjectID` inside of Azure using the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/api/resources/azure-ad-overview?view=graph-rest-1.0).
7173

@@ -81,10 +83,21 @@ Your Azure Service Principal will need the following _Application_ permission fo
8183

8284
Directory.Read.All (5778995a-e1bf-45b8-affa-663a9f3f4d04)
8385

84-
### Default
86+
#### Default
8587

8688
If no credentials are provided, the operator will create the `Role` and `ClusterRole` bindings using the values listed in the `owners` field.
8789

90+
### Docker Pull Secrets
91+
92+
By default, the operator will not create any secrets in the provisioned namespace.
93+
94+
**If the `PULL_SECRET_NAME` environment variable is set, the operator will copy your clusters pull secret to the provisioned namespace and patch the default service account.**
95+
96+
`PULL_SECRET_NAME` should be the name of the pull secret that exists in your cluster. By default, the operator will look for your secret in the `default` namespace.
97+
98+
To have the operator look in a different namespace for the pull secret, use the `PULL_SECRET_NAMESPACE` environment variable.
99+
100+
88101
## Creating a Sandbox
89102

90103
To create a Sandbox, apply a Sandbox CRD to the target cluster.

controller/sandbox.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package controller
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"log"
78
"os"
@@ -14,6 +15,7 @@ import (
1415
"k8s.io/apimachinery/pkg/api/resource"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617
"k8s.io/apimachinery/pkg/runtime"
18+
"k8s.io/apimachinery/pkg/types"
1719
ctrl "sigs.k8s.io/controller-runtime"
1820
"sigs.k8s.io/controller-runtime/pkg/client"
1921
"sigs.k8s.io/controller-runtime/pkg/client/config"
@@ -176,9 +178,59 @@ func (r *ReconcileSandbox) handleReconcile(ctx context.Context, request reconcil
176178
return fmt.Errorf("reconcile ClusterRoleBinding: %w", err)
177179
}
178180

181+
if os.Getenv("PULL_SECRET_NAME") != "" {
182+
secretName := os.Getenv("PULL_SECRET_NAME")
183+
secretData, err := getDockerSecretData(ctx, r.client, secretName)
184+
if err != nil {
185+
return fmt.Errorf("get secret data: %w", err)
186+
}
187+
188+
secret := getDockerSecret(sandbox, secretName, secretData)
189+
_, err = ctrl.CreateOrUpdate(ctx, r.client, &secret, func() error {
190+
return controllerutil.SetControllerReference(&sandbox, &secret, r.scheme)
191+
})
192+
if err != nil {
193+
return fmt.Errorf("reconcile docker Secret: %w", err)
194+
}
195+
196+
var defaultServiceAccount corev1.ServiceAccount
197+
if err := r.client.Get(ctx, types.NamespacedName{Name: "default", Namespace: namespace.Name}, &defaultServiceAccount); err != nil {
198+
return fmt.Errorf("get default service account: %w", err)
199+
}
200+
201+
patchBytes, err := getPatchBytes(secretName)
202+
if err != nil {
203+
return fmt.Errorf("get patch bytes: %w", err)
204+
}
205+
206+
patch := client.ConstantPatch(types.StrategicMergePatchType, patchBytes)
207+
if err := r.client.Patch(ctx, &defaultServiceAccount, patch, &client.PatchOptions{}); err != nil {
208+
return fmt.Errorf("patch service account: %w", err)
209+
}
210+
}
211+
179212
return nil
180213
}
181214

215+
func getPatchBytes(secretName string) ([]byte, error) {
216+
type imagePullSecretsPatch struct {
217+
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
218+
}
219+
220+
patch := imagePullSecretsPatch{
221+
ImagePullSecrets: []corev1.LocalObjectReference{
222+
{Name: secretName},
223+
},
224+
}
225+
226+
patchString, err := json.Marshal(patch)
227+
if err != nil {
228+
return nil, fmt.Errorf("patching default service account: %w", err)
229+
}
230+
231+
return patchString, nil
232+
}
233+
182234
func getNamespace(sandbox operatorsv1alpha1.Sandbox) corev1.Namespace {
183235
namespace := corev1.Namespace{
184236
ObjectMeta: metav1.ObjectMeta{
@@ -367,6 +419,41 @@ func getSmallResourceQuotaSpec() corev1.ResourceQuotaSpec {
367419
return resourceQuotaSpec
368420
}
369421

422+
func getDockerSecretData(ctx context.Context, client client.Client, secretName string) ([]byte, error) {
423+
var secretNamespace string
424+
if os.Getenv("PULL_SECRET_NAMESPACE") != "" {
425+
secretNamespace = os.Getenv("PULL_SECRET_NAMESPACE")
426+
} else {
427+
secretNamespace = "default"
428+
}
429+
430+
var dockerSecret corev1.Secret
431+
if err := client.Get(ctx, types.NamespacedName{Name: secretName, Namespace: secretNamespace}, &dockerSecret); err != nil {
432+
return nil, fmt.Errorf("get docker secret: %w", err)
433+
}
434+
435+
if _, ok := dockerSecret.Data[corev1.DockerConfigJsonKey]; !ok {
436+
return nil, fmt.Errorf("secret missing dockerconfig data")
437+
}
438+
439+
return dockerSecret.Data[corev1.DockerConfigJsonKey], nil
440+
}
441+
442+
func getDockerSecret(sandbox operatorsv1alpha1.Sandbox, secretName string, secretData []byte) corev1.Secret {
443+
dockerSecret := corev1.Secret{
444+
ObjectMeta: metav1.ObjectMeta{
445+
Name: secretName,
446+
Namespace: "sandbox-" + sandbox.Name,
447+
},
448+
Data: map[string][]byte{
449+
corev1.DockerConfigJsonKey: []byte(secretData),
450+
},
451+
Type: corev1.SecretTypeDockerConfigJson,
452+
}
453+
454+
return dockerSecret
455+
}
456+
370457
func getCommonLabels() map[string]string {
371458
commonLabels := make(map[string]string)
372459
commonLabels["app.kubernetes.io/name"] = "sandbox-operator"

deploy/cluster-role.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ rules:
1919
- secrets
2020
- namespaces
2121
- resourcequotas
22+
- serviceaccounts
2223
verbs:
2324
- '*'
2425
- apiGroups:

0 commit comments

Comments
 (0)