Skip to content

Commit 59a696e

Browse files
authored
Merge pull request #2988 from fabriziopandini/fix-clusterctl-soft-ownership
🐛fix a clusterctl error when moving cluster with user provided certs
2 parents 184c6f0 + a84d2b7 commit 59a696e

File tree

5 files changed

+124
-7
lines changed

5 files changed

+124
-7
lines changed

Diff for: cmd/clusterctl/client/cluster/objectgraph.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package cluster
1818

1919
import (
20-
"strings"
21-
2220
"github.com/pkg/errors"
2321
corev1 "k8s.io/api/core/v1"
2422
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -29,6 +27,7 @@ import (
2927
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3028
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
3129
logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
30+
secretutil "sigs.k8s.io/cluster-api/util/secret"
3231
"sigs.k8s.io/controller-runtime/pkg/client"
3332
)
3433

@@ -339,15 +338,15 @@ func (o *objectGraph) setSoftOwnership() {
339338
continue
340339
}
341340

342-
// If the secret name does not comply the naming convention {cluster-name}-{certificate-name/kubeconfig}, ignore it.
343-
nameSplit := strings.Split(secret.identity.Name, "-")
344-
if len(nameSplit) != 2 {
341+
// If the secret name is not a valid cluster secret name, ignore it.
342+
secretClusterName, _, err := secretutil.ParseSecretName(secret.identity.Name)
343+
if err != nil {
345344
continue
346345
}
347346

348-
// If the secret is linked to a cluster by the naming convention, then add the cluster to the list of the secrets's softOwners.
347+
// If the secret is linked to a cluster, then add the cluster to the list of the secrets's softOwners.
349348
for _, cluster := range clusters {
350-
if nameSplit[0] == cluster.identity.Name && secret.identity.Namespace == cluster.identity.Namespace {
349+
if secretClusterName == cluster.identity.Name && secret.identity.Namespace == cluster.identity.Namespace {
351350
secret.addSoftOwner(cluster)
352351
}
353352
}

Diff for: cmd/clusterctl/client/cluster/objectgraph_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,18 @@ func Test_objectGraph_setSoftOwnership(t *testing.T) {
10781078
"/v1, Kind=Secret, ns1/foo-kubeconfig": {}, // the kubeconfig secret has explicit OwnerRef to the cluster, so it should NOT be identified as a soft ownership
10791079
},
10801080
},
1081+
{
1082+
name: "A cluster with a soft owned secret (cluster name with - in the middle)",
1083+
fields: fields{
1084+
objs: test.NewFakeCluster("ns1", "foo-bar").Objs(),
1085+
},
1086+
wantSecrets: map[string][]string{ // wantSecrets is a map[node UID] --> list of soft owner UIDs
1087+
"/v1, Kind=Secret, ns1/foo-bar-ca": { // the ca secret has no explicit OwnerRef to the cluster, so it should be identified as a soft ownership
1088+
"cluster.x-k8s.io/v1alpha3, Kind=Cluster, ns1/foo-bar",
1089+
},
1090+
"/v1, Kind=Secret, ns1/foo-bar-kubeconfig": {}, // the kubeconfig secret has explicit OwnerRef to the cluster, so it should NOT be identified as a soft ownership
1091+
},
1092+
},
10811093
}
10821094
for _, tt := range tests {
10831095
t.Run(tt.name, func(t *testing.T) {

Diff for: util/secret/consts.go

+5
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ const (
4747
// APIServerEtcdClient is the secret name of user-supplied secret containing the apiserver-etcd-client key/cert
4848
APIServerEtcdClient Purpose = "apiserver-etcd-client"
4949
)
50+
51+
var (
52+
// allSecretPurposes defines a lists with all the secret suffix used by Cluster API
53+
allSecretPurposes = []Purpose{Kubeconfig, ClusterCA, EtcdCA, ServiceAccount, FrontProxyCA, APIServerEtcdClient}
54+
)

Diff for: util/secret/secret.go

+19
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package secret
1919
import (
2020
"context"
2121
"fmt"
22+
"strings"
2223

24+
"github.com/pkg/errors"
2325
corev1 "k8s.io/api/core/v1"
2426
"sigs.k8s.io/controller-runtime/pkg/client"
2527
)
@@ -50,3 +52,20 @@ func GetFromNamespacedName(ctx context.Context, c client.Client, clusterName cli
5052
func Name(cluster string, suffix Purpose) string {
5153
return fmt.Sprintf("%s-%s", cluster, suffix)
5254
}
55+
56+
// ParseSecretName return the cluster name and the suffix Purpose in name is a valid cluster secrets,
57+
// otherwise it return error.
58+
func ParseSecretName(name string) (string, Purpose, error) {
59+
separatorPos := strings.LastIndex(name, "-")
60+
if separatorPos == -1 {
61+
return "", "", errors.Errorf("%q is not a valid cluster secret name. The purpose suffix is missing", name)
62+
}
63+
clusterName := name[:separatorPos]
64+
purposeSuffix := Purpose(name[separatorPos+1:])
65+
for _, purpose := range allSecretPurposes {
66+
if purpose == purposeSuffix {
67+
return clusterName, purposeSuffix, nil
68+
}
69+
}
70+
return "", "", errors.Errorf("%q is not a valid cluster secret name. Invalid purpose suffix", name)
71+
}

Diff for: util/secret/secret_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package secret
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
)
24+
25+
func TestParseSecretName(t *testing.T) {
26+
type args struct {
27+
name string
28+
}
29+
tests := []struct {
30+
name string
31+
args args
32+
want string
33+
want1 Purpose
34+
wantErr bool
35+
}{
36+
{
37+
name: "A secret for the test cluster",
38+
args: args{
39+
name: "test-kubeconfig",
40+
},
41+
want: "test",
42+
want1: Kubeconfig,
43+
wantErr: false,
44+
},
45+
{
46+
name: "A secret for the test-capa cluster (cluster name with - in the middle)",
47+
args: args{
48+
name: "test-capa-ca",
49+
},
50+
want: "test-capa",
51+
want1: ClusterCA,
52+
wantErr: false,
53+
},
54+
{
55+
name: "Not a Cluster API secret",
56+
args: args{
57+
name: "foo",
58+
},
59+
wantErr: true,
60+
},
61+
{
62+
name: "Not a Cluster API secret (secret name with - in the middle)",
63+
args: args{
64+
name: "foo-bar",
65+
},
66+
wantErr: true,
67+
},
68+
}
69+
for _, tt := range tests {
70+
t.Run(tt.name, func(t *testing.T) {
71+
g := NewWithT(t)
72+
got, got1, err := ParseSecretName(tt.args.name)
73+
if tt.wantErr {
74+
g.Expect(err).To(HaveOccurred())
75+
} else {
76+
g.Expect(err).ToNot(HaveOccurred())
77+
}
78+
g.Expect(got).To(Equal(tt.want))
79+
g.Expect(got1).To(Equal(tt.want1))
80+
})
81+
}
82+
}

0 commit comments

Comments
 (0)