Skip to content

Commit bb3653f

Browse files
Merge pull request #1267 from ecordell/fix-bad-opgroup-annotations
Bug 1789920: Fix bad opgroup annotations
2 parents 224f57d + 813f844 commit bb3653f

File tree

3 files changed

+281
-1
lines changed

3 files changed

+281
-1
lines changed

pkg/controller/operators/olm/operator.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,11 @@ func (a *Operator) removeDanglingChildCSVs(csv *v1alpha1.ClusterServiceVersion)
910910
}
911911
}
912912

913+
if parent.GetNamespace() == csv.GetNamespace() {
914+
logger.Debug("deleting copied CSV since it has incorrect parent annotations")
915+
return a.deleteChild(csv, logger)
916+
}
917+
913918
return nil
914919
}
915920

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ func (a *Operator) annotateCSVs(group *v1.OperatorGroup, targetNamespaces []stri
183183
targetNamespaceSet := resolver.NewNamespaceSet(targetNamespaces)
184184

185185
for _, csv := range a.csvSet(group.GetNamespace(), v1alpha1.CSVPhaseAny) {
186+
if csv.IsCopied() {
187+
continue
188+
}
186189
logger := logger.WithField("csv", csv.GetName())
187190

188191
originalNamespacesAnnotation, _ := a.copyOperatorGroupAnnotations(&csv.ObjectMeta)[v1.OperatorGroupTargetsAnnotationKey]

test/e2e/operator_groups_e2e_test.go

Lines changed: 273 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ func TestOperatorGroupIntersection(t *testing.T) {
884884
// Generate operatorGroupD in namespaceD that selects namespace D and E
885885
// Generate csvD in namespaceD
886886
// Wait for csvD to be successful
887-
// Wait for csvD to have a CSV with copied status in namespace D
887+
// Wait for csvD to have a CSV with copied status in namespace E
888888
// Wait for operatorGroupD to have providedAPI annotation with crdD's Kind.version.group
889889
// Generate operatorGroupA in namespaceA that selects AllNamespaces
890890
// Generate csvD in namespaceA
@@ -895,6 +895,8 @@ func TestOperatorGroupIntersection(t *testing.T) {
895895
// Wait for csvA to be successful
896896
// Ensure clusterroles created and aggregated for accessing provided APIs
897897
// Wait for operatorGroupA to have providedAPI annotation with crdA's Kind.version.group in its providedAPIs annotation
898+
// Wait for csvA to have a CSV with copied status in namespace D
899+
// Ensure csvA retains the operatorgroup annotations for operatorgroupA
898900
// Wait for csvA to have a CSV with copied status in namespace C
899901
// Generate operatorGroupB in namespaceB that selects namespace C
900902
// Generate csvB in namespaceB that owns crdA
@@ -1091,6 +1093,26 @@ func TestOperatorGroupIntersection(t *testing.T) {
10911093
}
10921094
require.NoError(t, awaitAnnotations(t, q, map[string]string{v1.OperatorGroupProvidedAPIsAnnotationKey: kvgA}))
10931095

1096+
// Wait for csvA to have a CSV with copied status in namespace D
1097+
csvAinNsD, err := awaitCSV(t, crc, nsD, csvA.GetName(), csvCopiedChecker)
1098+
require.NoError(t, err)
1099+
1100+
// trigger a resync of operatorgropuD
1101+
fetchedGroupD, err := crc.OperatorsV1().OperatorGroups(nsD).Get(groupD.GetName(), metav1.GetOptions{})
1102+
require.NoError(t, err)
1103+
1104+
fetchedGroupD.Annotations["bump"] = "update"
1105+
_, err = crc.OperatorsV1().OperatorGroups(nsD).Update(fetchedGroupD)
1106+
require.NoError(t, err)
1107+
1108+
// Ensure csvA retains the operatorgroup annotations for operatorgroupA
1109+
csvAinNsD, err = awaitCSV(t, crc, nsD, csvA.GetName(), csvCopiedChecker)
1110+
require.NoError(t, err)
1111+
1112+
require.Equal(t, groupA.GetName(), csvAinNsD.Annotations[v1.OperatorGroupAnnotationKey])
1113+
require.Equal(t, nsA, csvAinNsD.Annotations[v1.OperatorGroupNamespaceAnnotationKey])
1114+
require.Equal(t, nsA, csvAinNsD.Labels[v1alpha1.CopiedLabelKey])
1115+
10941116
// Await csvA's copy in namespaceC
10951117
_, err = awaitCSV(t, crc, nsC, csvA.GetName(), csvCopiedChecker)
10961118
require.NoError(t, err)
@@ -1849,3 +1871,253 @@ func TestOperatorGroupInsufficientPermissionsResolveViaServiceAccountRemoval(t *
18491871
})
18501872
require.NoError(t, err)
18511873
}
1874+
1875+
// Versions of OLM at 0.14.1 and older had a bug that would place the wrong namespace annotation on copied CSVs,
1876+
// preventing them from being GCd. This ensures that any leftover CSVs in that state are properly cleared up.
1877+
func TestCleanupCsvsWithBadOwnerOperatorGroups(t *testing.T) {
1878+
defer cleaner.NotifyTestComplete(t, true)
1879+
c := newKubeClient(t)
1880+
crc := newCRClient(t)
1881+
csvName := genName("another-csv-") // must be lowercase for DNS-1123 validation
1882+
1883+
opGroupNamespace := testNamespace
1884+
matchingLabel := map[string]string{"inGroup": opGroupNamespace}
1885+
otherNamespaceName := genName(opGroupNamespace + "-")
1886+
1887+
t.Log("Creating CRD")
1888+
mainCRDPlural := genName("opgroup-")
1889+
mainCRD := newCRD(mainCRDPlural)
1890+
cleanupCRD, err := createCRD(c, mainCRD)
1891+
require.NoError(t, err)
1892+
defer cleanupCRD()
1893+
1894+
t.Logf("Getting default operator group 'global-operators' installed via operatorgroup-default.yaml %v", opGroupNamespace)
1895+
operatorGroup, err := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get("global-operators", metav1.GetOptions{})
1896+
require.NoError(t, err)
1897+
1898+
expectedOperatorGroupStatus := v1.OperatorGroupStatus{
1899+
Namespaces: []string{metav1.NamespaceAll},
1900+
}
1901+
1902+
t.Log("Waiting on operator group to have correct status")
1903+
err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {
1904+
fetched, fetchErr := crc.OperatorsV1().OperatorGroups(opGroupNamespace).Get(operatorGroup.Name, metav1.GetOptions{})
1905+
if fetchErr != nil {
1906+
return false, fetchErr
1907+
}
1908+
if len(fetched.Status.Namespaces) > 0 {
1909+
require.ElementsMatch(t, expectedOperatorGroupStatus.Namespaces, fetched.Status.Namespaces)
1910+
fmt.Println(fetched.Status.Namespaces)
1911+
return true, nil
1912+
}
1913+
return false, nil
1914+
})
1915+
require.NoError(t, err)
1916+
1917+
t.Log("Creating CSV")
1918+
// Generate permissions
1919+
serviceAccountName := genName("nginx-sa")
1920+
permissions := []v1alpha1.StrategyDeploymentPermissions{
1921+
{
1922+
ServiceAccountName: serviceAccountName,
1923+
Rules: []rbacv1.PolicyRule{
1924+
{
1925+
Verbs: []string{rbacv1.VerbAll},
1926+
APIGroups: []string{mainCRD.Spec.Group},
1927+
Resources: []string{mainCRDPlural},
1928+
},
1929+
},
1930+
},
1931+
}
1932+
1933+
serviceAccount := &corev1.ServiceAccount{
1934+
ObjectMeta: metav1.ObjectMeta{
1935+
Namespace: opGroupNamespace,
1936+
Name: serviceAccountName,
1937+
},
1938+
}
1939+
role := &rbacv1.Role{
1940+
ObjectMeta: metav1.ObjectMeta{
1941+
Namespace: opGroupNamespace,
1942+
Name: serviceAccountName + "-role",
1943+
},
1944+
Rules: permissions[0].Rules,
1945+
}
1946+
roleBinding := &rbacv1.RoleBinding{
1947+
ObjectMeta: metav1.ObjectMeta{
1948+
Namespace: opGroupNamespace,
1949+
Name: serviceAccountName + "-rb",
1950+
},
1951+
Subjects: []rbacv1.Subject{
1952+
{
1953+
Kind: "ServiceAccount",
1954+
Name: serviceAccountName,
1955+
Namespace: opGroupNamespace,
1956+
},
1957+
},
1958+
RoleRef: rbacv1.RoleRef{
1959+
Kind: "Role",
1960+
Name: role.GetName(),
1961+
},
1962+
}
1963+
_, err = c.CreateServiceAccount(serviceAccount)
1964+
require.NoError(t, err)
1965+
defer func() {
1966+
c.DeleteServiceAccount(serviceAccount.GetNamespace(), serviceAccount.GetName(), metav1.NewDeleteOptions(0))
1967+
}()
1968+
createdRole, err := c.CreateRole(role)
1969+
require.NoError(t, err)
1970+
defer func() {
1971+
c.DeleteRole(role.GetNamespace(), role.GetName(), metav1.NewDeleteOptions(0))
1972+
}()
1973+
createdRoleBinding, err := c.CreateRoleBinding(roleBinding)
1974+
require.NoError(t, err)
1975+
defer func() {
1976+
c.DeleteRoleBinding(roleBinding.GetNamespace(), roleBinding.GetName(), metav1.NewDeleteOptions(0))
1977+
}()
1978+
// Create a new NamedInstallStrategy
1979+
deploymentName := genName("operator-deployment")
1980+
namedStrategy := newNginxInstallStrategy(deploymentName, permissions, nil)
1981+
1982+
aCSV := newCSV(csvName, opGroupNamespace, "", semver.MustParse("0.0.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, namedStrategy)
1983+
aCSV.Labels = map[string]string{"label": t.Name()}
1984+
createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Create(&aCSV)
1985+
require.NoError(t, err)
1986+
1987+
err = ownerutil.AddOwnerLabels(createdRole, createdCSV)
1988+
require.NoError(t, err)
1989+
_, err = c.UpdateRole(createdRole)
1990+
require.NoError(t, err)
1991+
1992+
err = ownerutil.AddOwnerLabels(createdRoleBinding, createdCSV)
1993+
require.NoError(t, err)
1994+
_, err = c.UpdateRoleBinding(createdRoleBinding)
1995+
require.NoError(t, err)
1996+
1997+
t.Log("wait for CSV to succeed")
1998+
_, err = fetchCSV(t, crc, createdCSV.GetName(), opGroupNamespace, csvSucceededChecker)
1999+
require.NoError(t, err)
2000+
2001+
t.Log("wait for roles to be promoted to clusterroles")
2002+
var fetchedRole *rbacv1.ClusterRole
2003+
err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {
2004+
fetchedRole, err = c.GetClusterRole(role.GetName())
2005+
if err != nil {
2006+
if k8serrors.IsNotFound(err) {
2007+
return false, nil
2008+
}
2009+
return false, err
2010+
}
2011+
return true, nil
2012+
})
2013+
require.EqualValues(t, append(role.Rules, rbacv1.PolicyRule{
2014+
Verbs: []string{"get", "list", "watch"},
2015+
APIGroups: []string{""},
2016+
Resources: []string{"namespaces"},
2017+
}), fetchedRole.Rules)
2018+
var fetchedRoleBinding *rbacv1.ClusterRoleBinding
2019+
err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {
2020+
fetchedRoleBinding, err = c.GetClusterRoleBinding(roleBinding.GetName())
2021+
if err != nil {
2022+
if k8serrors.IsNotFound(err) {
2023+
return false, nil
2024+
}
2025+
return false, err
2026+
}
2027+
return true, nil
2028+
})
2029+
require.EqualValues(t, roleBinding.Subjects, fetchedRoleBinding.Subjects)
2030+
require.EqualValues(t, roleBinding.RoleRef.Name, fetchedRoleBinding.RoleRef.Name)
2031+
require.EqualValues(t, "rbac.authorization.k8s.io", fetchedRoleBinding.RoleRef.APIGroup)
2032+
require.EqualValues(t, "ClusterRole", fetchedRoleBinding.RoleRef.Kind)
2033+
2034+
t.Log("ensure operator was granted namespace list permission")
2035+
res, err := c.KubernetesInterface().AuthorizationV1().SubjectAccessReviews().Create(&authorizationv1.SubjectAccessReview{
2036+
Spec: authorizationv1.SubjectAccessReviewSpec{
2037+
User: "system:serviceaccount:" + opGroupNamespace + ":" + serviceAccountName,
2038+
ResourceAttributes: &authorizationv1.ResourceAttributes{
2039+
Group: corev1.GroupName,
2040+
Version: "v1",
2041+
Resource: "namespaces",
2042+
Verb: "list",
2043+
},
2044+
},
2045+
})
2046+
require.NoError(t, err)
2047+
require.True(t, res.Status.Allowed, "got %#v", res.Status)
2048+
2049+
t.Log("Waiting for operator namespace csv to have annotations")
2050+
err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {
2051+
fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Get(csvName, metav1.GetOptions{})
2052+
if fetchErr != nil {
2053+
if errors.IsNotFound(fetchErr) {
2054+
return false, nil
2055+
}
2056+
t.Logf("Error (in %v): %v", testNamespace, fetchErr.Error())
2057+
return false, fetchErr
2058+
}
2059+
if checkOperatorGroupAnnotations(fetchedCSV, operatorGroup, true, corev1.NamespaceAll) == nil {
2060+
return true, nil
2061+
}
2062+
return false, nil
2063+
})
2064+
require.NoError(t, err)
2065+
2066+
csvList, err := crc.OperatorsV1alpha1().ClusterServiceVersions(corev1.NamespaceAll).List(metav1.ListOptions{LabelSelector: fmt.Sprintf("label=%s", t.Name())})
2067+
require.NoError(t, err)
2068+
t.Logf("Found CSV count of %v", len(csvList.Items))
2069+
2070+
t.Logf("Create other namespace %s", otherNamespaceName)
2071+
otherNamespace := corev1.Namespace{
2072+
ObjectMeta: metav1.ObjectMeta{
2073+
Name: otherNamespaceName,
2074+
Labels: matchingLabel,
2075+
},
2076+
}
2077+
_, err = c.KubernetesInterface().CoreV1().Namespaces().Create(&otherNamespace)
2078+
require.NoError(t, err)
2079+
defer func() {
2080+
err = c.KubernetesInterface().CoreV1().Namespaces().Delete(otherNamespaceName, &metav1.DeleteOptions{})
2081+
require.NoError(t, err)
2082+
}()
2083+
2084+
t.Log("Waiting to ensure copied CSV shows up in other namespace")
2085+
err = wait.Poll(pollInterval, pollDuration, func() (bool, error) {
2086+
fetchedCSV, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(csvName, metav1.GetOptions{})
2087+
if fetchErr != nil {
2088+
if errors.IsNotFound(fetchErr) {
2089+
return false, nil
2090+
}
2091+
t.Logf("Error (in %v): %v", otherNamespaceName, fetchErr.Error())
2092+
return false, fetchErr
2093+
}
2094+
if checkOperatorGroupAnnotations(fetchedCSV, operatorGroup, false, "") == nil {
2095+
return true, nil
2096+
}
2097+
return false, nil
2098+
})
2099+
require.NoError(t, err)
2100+
2101+
// Give copied CSV a bad operatorgroup annotation
2102+
fetchedCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(csvName, metav1.GetOptions{})
2103+
require.NoError(t, err)
2104+
fetchedCSV.Annotations[v1.OperatorGroupNamespaceAnnotationKey] = fetchedCSV.GetNamespace()
2105+
_, err = crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Update(fetchedCSV)
2106+
require.NoError(t, err)
2107+
2108+
// wait for CSV to be gc'd
2109+
err = wait.Poll(pollInterval, 2*pollDuration, func() (bool, error) {
2110+
csv, fetchErr := crc.OperatorsV1alpha1().ClusterServiceVersions(otherNamespaceName).Get(csvName, metav1.GetOptions{})
2111+
if fetchErr != nil {
2112+
if errors.IsNotFound(fetchErr) {
2113+
return true, nil
2114+
}
2115+
t.Logf("Error (in %v): %v", opGroupNamespace, fetchErr.Error())
2116+
return false, fetchErr
2117+
}
2118+
t.Logf("%#v", csv.Annotations)
2119+
t.Logf(csv.GetNamespace())
2120+
return false, nil
2121+
})
2122+
require.NoError(t, err)
2123+
}

0 commit comments

Comments
 (0)