Skip to content

Commit dea0bc3

Browse files
committed
[edgefunc] Support for OCI creds via Secrets
1 parent 07ba2e7 commit dea0bc3

File tree

7 files changed

+115
-82
lines changed

7 files changed

+115
-82
lines changed

api/core/v1alpha/shared_types.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package v1alpha
2+
3+
// Group refers to an API Group. It must either be an empty string or a
4+
// RFC 1123 subdomain.
5+
//
6+
// This validation is based off of the corresponding Kubernetes validation:
7+
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L208
8+
//
9+
// Valid values include:
10+
//
11+
// * "" - empty string implies core Kubernetes API group
12+
// * "gateway.networking.k8s.io"
13+
// * "controllers.apoxy.dev"
14+
//
15+
// Invalid values include:
16+
//
17+
// * "example.com/bar" - "/" is an invalid character
18+
//
19+
// +kubebuilder:validation:MaxLength=253
20+
// +kubebuilder:validation:Pattern=`^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
21+
type Group string
22+
23+
// Kind refers to an API Kind. It must be a valid Kubernetes kind.
24+
//
25+
// Valid values include:
26+
//
27+
// * "Service"
28+
// * "HTTPRoute"
29+
// * "Proxy"
30+
//
31+
// Invalid values include:
32+
//
33+
// * "invalid/kind" - "/" is an invalid character
34+
//
35+
// +kubebuilder:validation:MinLength=1
36+
// +kubebuilder:validation:MaxLength=63
37+
// +kubebuilder:validation:Pattern=`^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$`
38+
type Kind string
39+
40+
// ObjectName refers to the name of an API object. It must be a valid Kubernetes
41+
// name.
42+
//
43+
// Object names can have a variety of forms, including RFC 1123 subdomains,
44+
// RFC 1123 labels, or RFC 1035 labels.
45+
//
46+
// +kubebuilder:validation:MinLength=1
47+
// +kubebuilder:validation:MaxLength=253
48+
type ObjectName string
49+
50+
// Namespace refers to a Kubernetes namespace. It must be a RFC 1123 label.
51+
//
52+
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
53+
// +kubebuilder:validation:MinLength=1
54+
// +kubebuilder:validation:MaxLength=63
55+
type Namespace string

api/extensions/v1alpha1/edgefunction_types.go

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"k8s.io/apiserver/pkg/registry/rest"
88
"sigs.k8s.io/apiserver-runtime/pkg/builder/resource"
99

10-
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
10+
corev1alpha "github.com/apoxy-dev/apoxy-cli/api/core/v1alpha"
1111
)
1212

1313
type SourceFile struct {
@@ -73,15 +73,17 @@ type WasmSource struct {
7373

7474
type OCICredentialsObjectReference struct {
7575
// Group is the group of the target resource.
76-
// Currently only controllers.apoxy.dev/v1alpha1 is supported.
77-
Group gwapiv1.Group `json:"group"`
76+
Group corev1alpha.Group `json:"group"`
7877

7978
// Kind is kind of the target resource.
80-
// Supports Secret with on-prem deploys and
81-
Kind gwapiv1.Kind `json:"kind"`
79+
// Supports Secret with on-prem deploys.
80+
Kind corev1alpha.Kind `json:"kind"`
8281

8382
// Name is the name of the target resource.
84-
Name gwapiv1.ObjectName `json:"name"`
83+
Name corev1alpha.ObjectName `json:"name"`
84+
85+
// Namespace is the namespace of the target resource.
86+
Namespace corev1alpha.Namespace `json:"namespace"`
8587
}
8688

8789
type OCICredentials struct {
@@ -197,19 +199,6 @@ type EdgeFunctionRuntime struct {
197199
Port *int32 `json:"port,omitempty"`
198200
}
199201

200-
type EdgeFunctionTargetReference struct {
201-
// Group is the group of the target resource.
202-
// Currently only controllers.apoxy.dev/v1alpha1 is supported.
203-
Group gwapiv1.Group `json:"group"`
204-
205-
// Kind is kind of the target resource.
206-
// Currently only Proxy is supported.
207-
Kind gwapiv1.Kind `json:"kind"`
208-
209-
// Name is the name of the target resource.
210-
Name gwapiv1.ObjectName `json:"name"`
211-
}
212-
213202
type EdgeFunctionMode string
214203

215204
const (

api/extensions/v1alpha1/zz_generated.deepcopy.go

Lines changed: 0 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/generated/zz_generated.openapi.go

Lines changed: 11 additions & 41 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/apiserver/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,15 @@ func main() {
9191
}
9292
}()
9393

94+
var kc *rest.Config
9495
rC := apiserver.NewClientConfig()
9596
if *inCluster {
9697
rC, err = rest.InClusterConfig()
9798
if err != nil {
9899
log.Errorf("failed to create in-cluster k8s config: %v", err)
99100
ctxCancel(&startErr{Err: err})
100101
}
102+
kc = rC
101103
}
102104
m := apiserver.New()
103105
go func() {
@@ -140,7 +142,7 @@ func main() {
140142
}
141143
w := tworker.New(tc, ingest.EdgeFunctionIngestQueue, wOpts)
142144
ingest.RegisterWorkflows(w)
143-
ww := ingest.NewWorker(a3y, *ingestStoreDir)
145+
ww := ingest.NewWorker(kc, a3y, *ingestStoreDir)
144146
ww.RegisterActivities(w)
145147
go func() {
146148
if err = ww.ListenAndServeEdgeFuncs("" /* host */, *ingestStorePort); err != nil {

pkg/apiserver/ingest/edgefunction.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import (
2222
"go.temporal.io/sdk/temporal"
2323
tworker "go.temporal.io/sdk/worker"
2424
"go.temporal.io/sdk/workflow"
25+
k8scorev1 "k8s.io/api/core/v1"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/client-go/kubernetes"
28+
"k8s.io/client-go/rest"
2629
"k8s.io/client-go/util/retry"
2730
"oras.land/oras-go/v2"
2831
"oras.land/oras-go/v2/content"
@@ -185,16 +188,19 @@ Finalize:
185188

186189
// Worker implements Temporal Activities for Edge Functions Ingest queue.
187190
type worker struct {
191+
k8s kubernetes.Interface
188192
a3y versioned.Interface
189193
baseDir string
190194
}
191195

192196
// NewWorker returns a new worker for Edge Functions Ingest queue.
193-
func NewWorker(c versioned.Interface, baseDir string) *worker {
194-
return &worker{
197+
func NewWorker(kc *rest.Config, c versioned.Interface, baseDir string) *worker {
198+
w := &worker{
195199
a3y: c,
196200
baseDir: baseDir,
197201
}
202+
w.k8s = kubernetes.NewForConfigOrDie(kc)
203+
return w
198204
}
199205

200206
// RegisterActivities registers Edge Functions Ingest activities with
@@ -405,8 +411,35 @@ func (w *worker) pullOCIImage(
405411
Password: string(pwd),
406412
},
407413
)
408-
} else if ociRef.CredentialsRef != nil {
409-
// TODO(dsky): Implement credentials ref.
414+
} else if secretRef := ociRef.CredentialsRef; secretRef != nil {
415+
if secretRef.Group == "" && secretRef.Kind == "Secret" {
416+
// Secret refs are only valid in k8s environment.
417+
if w.k8s == nil {
418+
return nil, errors.New("k8s environment is not set")
419+
}
420+
421+
secret, err := w.k8s.CoreV1().Secrets(string(secretRef.Namespace)).Get(ctx, string(secretRef.Name), metav1.GetOptions{})
422+
if err != nil {
423+
return nil, fmt.Errorf("failed to get secret: %w", err)
424+
}
425+
if secret.Type != k8scorev1.SecretTypeDockerConfigJson {
426+
return nil, fmt.Errorf("invalid secret type %q, expected %q", secret.Type, "kubernetes.io/dockerconfigjson")
427+
}
428+
encodedToken := secret.Data[".dockerconfigjson"]
429+
var dockerConfig struct {
430+
Auths map[string]auth.Credential `json:"auths"`
431+
}
432+
if err := json.Unmarshal(encodedToken, &dockerConfig); err != nil {
433+
return nil, fmt.Errorf("failed to parse dockerconfigjson: %w", err)
434+
}
435+
436+
credsFunc = func(_ context.Context, _ string) (auth.Credential, error) {
437+
return dockerConfig.Auths[repo.Reference.Registry], nil
438+
}
439+
} else {
440+
// TODO(dilyevsky): Support other kinds of secrets for non-k8s environments.
441+
return nil, fmt.Errorf("invalid secret kind %q, expected %q", secretRef.Kind, "Secret")
442+
}
410443
} else {
411444
log.Debug("No credentials provided for OCI registry")
412445
}

pkg/cmd/alpha/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ allowing you to test and develop your proxy infrastructure.`,
271271
}
272272
w := tworker.New(tc, ingest.EdgeFunctionIngestQueue, wOpts)
273273
ingest.RegisterWorkflows(w)
274-
ww := ingest.NewWorker(c, os.Getenv("TMPDIR"))
274+
ww := ingest.NewWorker(nil /* no k8s client in local mode */, c, os.Getenv("TMPDIR"))
275275
ww.RegisterActivities(w)
276276
go func() {
277277
if err = ww.ListenAndServeEdgeFuncs("localhost", 8081); err != nil {

0 commit comments

Comments
 (0)