Skip to content

Commit

Permalink
Merge pull request #144 from turkenh/sync-upstream-main
Browse files Browse the repository at this point in the history
Sync upstream main
  • Loading branch information
turkenh authored Sep 24, 2024
2 parents 7c58e0c + a11db05 commit 64705d9
Show file tree
Hide file tree
Showing 20 changed files with 483 additions and 65 deletions.
12 changes: 10 additions & 2 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,17 @@ go-build:
CACHE --id go-build --sharing shared /root/.cache/go-build
COPY --dir apis/ cmd/ internal/ pkg/ .
RUN go build -o crossplane${ext} ./cmd/crossplane
RUN sha256sum crossplane${ext} | head -c 64 > crossplane${ext}.sha256
RUN go build -o crank${ext} ./cmd/crank
SAVE ARTIFACT crossplane${ext} AS LOCAL _output/bin/${GOOS}_${GOARCH}/crossplane${ext}
SAVE ARTIFACT crank${ext} AS LOCAL _output/bin/${GOOS}_${GOARCH}/crank${ext}
RUN sha256sum crank${ext} | head -c 64 > crank${ext}.sha256
RUN tar -czvf crank.tar.gz crank${ext} crank${ext}.sha256
RUN sha256sum crank.tar.gz | head -c 64 > crank.tar.gz.sha256
SAVE ARTIFACT --keep-ts crossplane${ext} AS LOCAL _output/bin/${GOOS}_${GOARCH}/crossplane${ext}
SAVE ARTIFACT --keep-ts crossplane${ext}.sha256 AS LOCAL _output/bin/${GOOS}_${GOARCH}/crossplane${ext}.sha256
SAVE ARTIFACT --keep-ts crank${ext} AS LOCAL _output/bin/${GOOS}_${GOARCH}/crank${ext}
SAVE ARTIFACT --keep-ts crank${ext}.sha256 AS LOCAL _output/bin/${GOOS}_${GOARCH}/crank${ext}.sha256
SAVE ARTIFACT --keep-ts crank.tar.gz AS LOCAL _output/bundle/${GOOS}_${GOARCH}/crank.tar.gz
SAVE ARTIFACT --keep-ts crank.tar.gz.sha256 AS LOCAL _output/bundle/${GOOS}_${GOARCH}/crank.tar.gz.sha256

# go-multiplatform-build builds Crossplane binaries for all supported OS
# and architectures.
Expand Down
2 changes: 1 addition & 1 deletion apis/pkg/v1beta1/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ type Dependency struct {
// Type is the type of package. Can be either Configuration or Provider.
Type PackageType `json:"type"`

// Constraints is a valid semver range, which will be used to select a valid
// Constraints is a valid semver range or a digest, which will be used to select a valid
// dependency version.
Constraints string `json:"constraints"`
}
Expand Down
2 changes: 1 addition & 1 deletion cluster/crds/pkg.crossplane.io_locks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ spec:
properties:
constraints:
description: |-
Constraints is a valid semver range, which will be used to select a valid
Constraints is a valid semver range or a digest, which will be used to select a valid
dependency version.
type: string
package:
Expand Down
31 changes: 23 additions & 8 deletions cmd/crank/render/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/alecthomas/kong"
"github.com/spf13/afero"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/serializer/json"

Expand All @@ -48,9 +49,10 @@ type Cmd struct {
ContextValues map[string]string `help:"Comma-separated context key-value pairs to pass to the Function pipeline. Values must be JSON. Keys take precedence over --context-files." mapsep:""`
IncludeFunctionResults bool `help:"Include informational and warning messages from Functions in the rendered output as resources of kind: Result." short:"r"`
IncludeFullXR bool `help:"Include a direct copy of the input XR's spec and metadata fields in the rendered output." short:"x"`
ObservedResources string `help:"A YAML file or directory of YAML files specifying the observed state of composed resources." placeholder:"PATH" short:"o" type:"path"`
ExtraResources string `help:"A YAML file or directory of YAML files specifying extra resources to pass to the Function pipeline." placeholder:"PATH" short:"e" type:"path"`
ObservedResources string `help:"A YAML file or directory of YAML files specifying the observed state of composed resources." placeholder:"PATH" short:"o" type:"path"`
ExtraResources string `help:"A YAML file or directory of YAML files specifying extra resources to pass to the Function pipeline." placeholder:"PATH" short:"e" type:"path"`
IncludeContext bool `help:"Include the context in the rendered output as a resource of kind: Context." short:"c"`
FunctionCredentials string `help:"A YAML file or directory of YAML files specifying credentials to use for Functions to render the XR." placeholder:"PATH" type:"path"`

Timeout time.Duration `default:"1m" help:"How long to run before timing out."`

Expand Down Expand Up @@ -109,6 +111,10 @@ Examples:
# Pass extra resources Functions in the pipeline can request.
crossplane render xr.yaml composition.yaml functions.yaml \
--extra-resources=extra-resources.yaml
# Pass credentials to Functions in the pipeline that need them.
crossplane render xr.yaml composition.yaml functions.yaml \
--function-credentials=credentials.yaml
`
}

Expand Down Expand Up @@ -149,6 +155,14 @@ func (c *Cmd) Run(k *kong.Context, log logging.Logger) error { //nolint:gocognit
return errors.Wrapf(err, "cannot load functions from %q", c.Functions)
}

fcreds := []corev1.Secret{}
if c.FunctionCredentials != "" {
fcreds, err = LoadCredentials(c.fs, c.FunctionCredentials)
if err != nil {
return errors.Wrapf(err, "cannot load secrets from %q", c.FunctionCredentials)
}
}

ors := []composed.Unstructured{}
if c.ObservedResources != "" {
ors, err = LoadObservedResources(c.fs, c.ObservedResources)
Expand Down Expand Up @@ -181,12 +195,13 @@ func (c *Cmd) Run(k *kong.Context, log logging.Logger) error { //nolint:gocognit
defer cancel()

out, err := Render(ctx, log, Inputs{
CompositeResource: xr,
Composition: comp,
Functions: fns,
ObservedResources: ors,
ExtraResources: ers,
Context: fctx,
CompositeResource: xr,
Composition: comp,
Functions: fns,
FunctionCredentials: fcreds,
ObservedResources: ors,
ExtraResources: ers,
Context: fctx,
})
if err != nil {
return errors.Wrap(err, "cannot render composite resource")
Expand Down
20 changes: 20 additions & 0 deletions cmd/crank/render/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"path/filepath"

"github.com/spf13/afero"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"

Expand Down Expand Up @@ -94,6 +95,25 @@ func LoadFunctions(filesys afero.Fs, file string) ([]pkgv1.Function, error) {
return functions, nil
}

// LoadCredentials from a stream of YAML manifests.
func LoadCredentials(fs afero.Fs, file string) ([]corev1.Secret, error) {
stream, err := LoadYAMLStream(fs, file)
if err != nil {
return nil, errors.Wrap(err, "cannot load YAML stream from file")
}

secrets := make([]corev1.Secret, 0, len(stream))
for _, y := range stream {
s := &corev1.Secret{}
if err := yaml.Unmarshal(y, s); err != nil {
return nil, errors.Wrap(err, "cannot parse YAML secret manifest")
}
secrets = append(secrets, *s)
}

return secrets, nil
}

// LoadExtraResources from a stream of YAML manifests.
func LoadExtraResources(fs afero.Fs, file string) ([]unstructured.Unstructured, error) {
stream, err := LoadYAMLStream(fs, file)
Expand Down
44 changes: 38 additions & 6 deletions cmd/crank/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/types/known/structpb"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
Expand Down Expand Up @@ -66,12 +67,13 @@ const (

// Inputs contains all inputs to the render process.
type Inputs struct {
CompositeResource *ucomposite.Unstructured
Composition *apiextensionsv1.Composition
Functions []pkgv1.Function
ObservedResources []composed.Unstructured
ExtraResources []unstructured.Unstructured
Context map[string][]byte
CompositeResource *ucomposite.Unstructured
Composition *apiextensionsv1.Composition
Functions []pkgv1.Function
FunctionCredentials []corev1.Secret
ObservedResources []composed.Unstructured
ExtraResources []unstructured.Unstructured
Context map[string][]byte

// TODO(negz): Allow supplying observed XR and composed resource connection
// details. Maybe as Secrets? What if secret stores are in use?
Expand Down Expand Up @@ -159,6 +161,16 @@ func (r *RuntimeFunctionRunner) Stop(ctx context.Context) error {
return nil
}

// getSecret retrieves the secret with the specified name and namespace from the provided list of secrets.
func getSecret(name string, nameSpace string, secrets []corev1.Secret) (*corev1.Secret, error) {
for _, s := range secrets {
if s.GetName() == name && s.GetNamespace() == nameSpace {
return &s, nil
}
}
return nil, errors.Errorf("secret %q not found", name)
}

// Render the desired XR and composed resources, sorted by resource name, given the supplied inputs.
func Render(ctx context.Context, log logging.Logger, in Inputs) (Outputs, error) { //nolint:gocognit // TODO(negz): Should we refactor to break this up a bit?
runtimes, err := NewRuntimeFunctionRunner(ctx, log, in.Functions)
Expand Down Expand Up @@ -227,6 +239,26 @@ func Render(ctx context.Context, log logging.Logger, in Inputs) (Outputs, error)
req.Input = in
}

req.Credentials = map[string]*fnv1.Credentials{}
for _, cs := range fn.Credentials {
// For now we only support loading credentials from secrets.
if cs.Source != apiextensionsv1.FunctionCredentialsSourceSecret || cs.SecretRef == nil {
continue
}

s, err := getSecret(cs.SecretRef.Name, cs.SecretRef.Namespace, in.FunctionCredentials)
if err != nil {
return Outputs{}, errors.Wrapf(err, "cannot get credentials from secret %q", cs.SecretRef.Name)
}
req.Credentials[cs.Name] = &fnv1.Credentials{
Source: &fnv1.Credentials_CredentialData{
CredentialData: &fnv1.CredentialData{
Data: s.Data,
},
},
}
}

rsp, err := runner.RunFunction(ctx, fn.FunctionRef.Name, req)
if err != nil {
return Outputs{}, errors.Wrapf(err, "cannot run pipeline step %q", fn.Step)
Expand Down
53 changes: 53 additions & 0 deletions cmd/crank/render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -1046,6 +1047,58 @@ func TestFilterExtraResources(t *testing.T) {
}
}

func TestGetSecret(t *testing.T) {
secrets := []corev1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "secret1",
Namespace: "namespace1",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "secret2",
Namespace: "namespace2",
},
},
}

tests := map[string]struct {
name string
namespace string
secrets []corev1.Secret
wantErr bool
}{
"SecretFound": {
name: "secret1",
namespace: "namespace1",
secrets: secrets,
wantErr: false,
},
"SecretNotFound": {
name: "secret3",
namespace: "namespace3",
secrets: secrets,
wantErr: true,
},
"SecretWrongNamespace": {
name: "secret1",
namespace: "namespace2",
secrets: secrets,
wantErr: true,
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
_, err := getSecret(tc.name, tc.namespace, tc.secrets)
if (err != nil) != tc.wantErr {
t.Errorf("getSecret() error = %v, wantErr %v", err, tc.wantErr)
}
})
}
}

func MustStructJSON(j string) *structpb.Struct {
s := &structpb.Struct{}
if err := protojson.Unmarshal([]byte(j), s); err != nil {
Expand Down
28 changes: 28 additions & 0 deletions internal/controller/pkg/manager/revisioner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
conregv1 "github.com/google/go-containerregistry/pkg/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -96,6 +97,33 @@ func TestPackageRevisioner(t *testing.T) {
digest: "return-me",
},
},
"SuccessfulDigest": {
reason: "Should return the digest of the package source image.",
args: args{
pkg: &v1.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "provider-nop",
},
Spec: v1.ProviderSpec{
PackageSpec: v1.PackageSpec{
Package: "crossplane-contrib/provider-nop@sha256:ecc25c121431dfc7058754427f97c034ecde26d4aafa0da16d258090e0443904",
PackagePullPolicy: &pullIfNotPresent,
},
},
},
f: &fake.MockFetcher{
MockHead: fake.NewMockHeadFn(&conregv1.Descriptor{
Digest: conregv1.Hash{
Algorithm: "sha256",
Hex: "ecc25c121431dfc7058754427f97c034ecde26d4aafa0da16d258090e0443904",
},
}, nil),
},
},
want: want{
digest: "provider-nop-ecc25c121431",
},
},
"ErrParseRef": {
reason: "Should return an error if we cannot parse reference from package source image.",
args: args{
Expand Down
Loading

0 comments on commit 64705d9

Please sign in to comment.