Skip to content

Commit 8dd587f

Browse files
Merge pull request #307 from perdasilva/bz_1982737
Bug 1982737: Make malformed CSV fail nicely (#2673)
2 parents ec696d9 + a811ba2 commit 8dd587f

File tree

8 files changed

+178
-2
lines changed

8 files changed

+178
-2
lines changed

staging/operator-lifecycle-manager/pkg/controller/errors/errors.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ func IsMultipleExistingCRDOwnersError(err error) bool {
4747
return false
4848
}
4949

50+
type FatalError struct {
51+
error
52+
}
53+
54+
func NewFatalError(err error) FatalError {
55+
return FatalError{err}
56+
}
57+
func IsFatal(err error) bool {
58+
switch err.(type) {
59+
case FatalError:
60+
return true
61+
}
62+
return false
63+
}
64+
5065
// GroupVersionKindNotFoundError occurs when we can't find an API via discovery
5166
type GroupVersionKindNotFoundError struct {
5267
Group string

staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,13 @@ type UnpackedBundleReference struct {
13761376
Properties string `json:"properties"`
13771377
}
13781378

1379-
// unpackBundles makes one walk through the bundlelookups and attempts to progress them
1379+
/* unpackBundles makes one walk through the bundlelookups and attempts to progress them
1380+
Returns:
1381+
unpacked: bool - If the bundle was successfully unpacked
1382+
out: *v1alpha1.InstallPlan - the resulting installPlan
1383+
error: error
1384+
*/
1385+
13801386
func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.InstallPlan, error) {
13811387
out := plan.DeepCopy()
13821388
unpacked := true
@@ -1427,6 +1433,10 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
14271433
// Ensure that bundle can be applied by the current version of OLM by converting to steps
14281434
steps, err := resolver.NewStepsFromBundle(res.Bundle(), out.GetNamespace(), res.Replaces, res.CatalogSourceRef.Name, res.CatalogSourceRef.Namespace)
14291435
if err != nil {
1436+
if fatal := olmerrors.IsFatal(err); fatal {
1437+
return false, nil, err
1438+
}
1439+
14301440
errs = append(errs, fmt.Errorf("failed to turn bundle into steps: %v", err))
14311441
unpacked = false
14321442
continue
@@ -1615,6 +1625,14 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
16151625
if len(plan.Status.BundleLookups) > 0 {
16161626
unpacked, out, err := o.unpackBundles(plan)
16171627
if err != nil {
1628+
// If the error was fatal capture and fail
1629+
if fatal := olmerrors.IsFatal(err); fatal {
1630+
if err := o.transitionInstallPlanToFailed(plan, logger, v1alpha1.InstallPlanReasonInstallCheckFailed, err.Error()); err != nil {
1631+
// retry for failure to update status
1632+
syncError = err
1633+
return
1634+
}
1635+
}
16181636
// Retry sync if non-fatal error
16191637
syncError = fmt.Errorf("bundle unpacking failed: %v", err)
16201638
return

staging/operator-lifecycle-manager/pkg/controller/registry/resolver/steps.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"strings"
88

9+
olmerrors "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors"
910
"github.com/operator-framework/operator-registry/pkg/api"
1011
extScheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -118,6 +119,15 @@ func NewStepResourceFromBundle(bundle *api.Bundle, namespace, replaces, catalogS
118119
return nil, err
119120
}
120121

122+
// Check unpacked bundled for for missing APIVersion or Kind
123+
if csv.APIVersion == "" {
124+
return nil, olmerrors.NewFatalError(fmt.Errorf("bundle CSV %s missing APIVersion", csv.Name))
125+
}
126+
127+
if csv.Kind == "" {
128+
return nil, olmerrors.NewFatalError(fmt.Errorf("bundle CSV %s missing Kind", csv.Name))
129+
}
130+
121131
csv.SetNamespace(namespace)
122132
csv.Spec.Replaces = replaces
123133
anno, err := projection.PropertiesAnnotationFromPropertyList(bundle.Properties)

staging/operator-lifecycle-manager/test/e2e/catalog_e2e_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77
"context"
88
"fmt"
99
"net"
10+
"path/filepath"
1011
"strconv"
1112
"strings"
1213
"time"
1314

1415
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
16+
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
1517
k8serror "k8s.io/apimachinery/pkg/api/errors"
1618
"sigs.k8s.io/controller-runtime/pkg/client"
1719

@@ -40,6 +42,7 @@ import (
4042
const (
4143
openshiftregistryFQDN = "image-registry.openshift-image-registry.svc:5000"
4244
catsrcImage = "docker://quay.io/olmtest/catsrc-update-test:"
45+
badCSVDir = "bad-csv"
4346
)
4447

4548
var _ = Describe("Starting CatalogSource e2e tests", func() {
@@ -1340,6 +1343,68 @@ var _ = Describe("Starting CatalogSource e2e tests", func() {
13401343
}
13411344
}
13421345
})
1346+
1347+
When("A CatalogSource is created with an operator that has a CSV with missing metadata.ApiVersion", func() {
1348+
1349+
var (
1350+
magicCatalog MagicCatalog
1351+
catalogSourceName string
1352+
subscription *operatorsv1alpha1.Subscription
1353+
c client.Client
1354+
)
1355+
1356+
BeforeEach(func() {
1357+
c = ctx.Ctx().Client()
1358+
1359+
provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, badCSVDir, "bad-csv.yaml"))
1360+
Expect(err).To(BeNil())
1361+
1362+
catalogSourceName = genName("cat-bad-csv")
1363+
magicCatalog = NewMagicCatalog(c, ns.GetName(), catalogSourceName, provider)
1364+
Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())
1365+
1366+
})
1367+
1368+
AfterEach(func() {
1369+
TeardownNamespace(ns.GetName())
1370+
})
1371+
1372+
When("A Subscription is created catalogSource built with the malformed CSV", func() {
1373+
BeforeEach(func () {
1374+
subscription = &operatorsv1alpha1.Subscription{
1375+
ObjectMeta: metav1.ObjectMeta{
1376+
Name: fmt.Sprintf("%s-sub", catalogSourceName),
1377+
Namespace: ns.GetName(),
1378+
},
1379+
Spec: &operatorsv1alpha1.SubscriptionSpec{
1380+
CatalogSource: catalogSourceName,
1381+
CatalogSourceNamespace: ns.GetName(),
1382+
Channel: "stable",
1383+
Package: "packageA",
1384+
},
1385+
}
1386+
Expect(c.Create(context.Background(), subscription)).To(BeNil())
1387+
})
1388+
1389+
It("fails with a ResolutionFailed error condition, and a message that highlights the missing field in the CSV", func() {
1390+
1391+
subscription, err := fetchSubscription(crc, subscription.GetNamespace(), subscription.GetName(), subscriptionHasInstallPlanChecker)
1392+
Expect(err).Should(BeNil())
1393+
installPlanName := subscription.Status.Install.Name
1394+
1395+
// ensure we wait for the installPlan to fail before moving forward then fetch the subscription again
1396+
_, err = fetchInstallPlan(GinkgoT(), crc, installPlanName, subscription.GetNamespace(), buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed))
1397+
Expect(err).To(BeNil())
1398+
subscription, err = fetchSubscription(crc, subscription.GetNamespace(), subscription.GetName(), subscriptionHasInstallPlanChecker)
1399+
Expect(err).To(BeNil())
1400+
1401+
// expect the message that API missing
1402+
failingCondition := subscription.Status.GetCondition(operatorsv1alpha1.SubscriptionInstallPlanFailed)
1403+
Expect(failingCondition.Message).To(ContainSubstring("missing APIVersion"))
1404+
})
1405+
})
1406+
})
1407+
13431408
})
13441409

13451410
func getOperatorDeployment(c operatorclient.ClientInterface, namespace string, operatorLabels labels.Set) (*appsv1.Deployment, error) {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
schema: olm.package
3+
name: packageA
4+
defaultChannel: stable
5+
---
6+
schema: olm.channel
7+
package: packageA
8+
name: stable
9+
entries:
10+
- name: bad-csv
11+
---
12+
schema: olm.bundle
13+
name: bad-csv
14+
package: packageA
15+
image: quay.io/olmtest/missing_api_version:latest
16+
properties:
17+
- type: olm.gvk
18+
value:
19+
group: example.com
20+
kind: TestA
21+
version: v1alpha1
22+
- type: olm.package
23+
value:
24+
packageName: packageA
25+
version: 1.0.0

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors/errors.go

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

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/steps.go

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

0 commit comments

Comments
 (0)