Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/project/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,13 @@ func packageHasHealthyConditions(pkg xpkgv1.PackageRevision) bool {
// ApplyResources installs arbitrary resources to the target control plane.
func ApplyResources(ctx context.Context, cl client.Client, resources []runtime.RawExtension) error {
for _, raw := range resources {
// Skip empty documents. A YAML manifest can contain documents that hold
// no resource — e.g. a leading comment block before the first "---"
// separator, or "---\n---" between two resources — which unmarshal to an
// empty RawExtension. kubectl skips these; do the same rather than
// rejecting an otherwise valid manifest.
if len(raw.Raw) == 0 {
return errors.New("encountered an invalid or empty raw resource")
continue
}

obj := &unstructured.Unstructured{}
Expand Down
40 changes: 32 additions & 8 deletions internal/project/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -103,15 +104,38 @@ func TestApplyResources(t *testing.T) {
}
})

t.Run("EmptyRawErrors", func(t *testing.T) {
t.Parallel()
// Empty documents (e.g. a leading comment block before the first "---")
// unmarshal to an empty RawExtension. They must be skipped rather than
// rejected, and resources alongside them still applied.
skipCases := map[string]struct {
reason string
resources []runtime.RawExtension
wantErr error
wantName string
}{
"SkipsEmptyDocumentAndAppliesRest": {
reason: "An empty document alongside a real resource is skipped, and the real resource is still applied.",
resources: []runtime.RawExtension{{}, {Raw: configMapJSON("real")}},
wantErr: nil,
wantName: "real",
},
}
for name, tc := range skipCases {
t.Run(name, func(t *testing.T) {
t.Parallel()

cl := newFakeClient(t)
err := ApplyResources(t.Context(), cl, []runtime.RawExtension{{}})
if err == nil {
t.Fatal("expected error for empty raw, got nil")
}
})
cl := newFakeClient(t)
err := ApplyResources(t.Context(), cl, tc.resources)
if diff := cmp.Diff(tc.wantErr, err, cmpopts.EquateErrors()); diff != "" {
t.Errorf("%s\nApplyResources(...): -want error, +got error:\n%s", tc.reason, diff)
}

var got corev1.ConfigMap
if err := cl.Get(t.Context(), types.NamespacedName{Name: tc.wantName, Namespace: "default"}, &got); err != nil {
t.Errorf("%s\nconfigmap %q not found: %v", tc.reason, tc.wantName, err)
}
})
}
}

func TestLookupLockPackage(t *testing.T) {
Expand Down
Loading