@@ -18,7 +18,12 @@ package controllers
18
18
19
19
import (
20
20
"context"
21
+ "crypto/rand"
22
+ "crypto/rsa"
23
+ "crypto/x509"
24
+ "crypto/x509/pkix"
21
25
"fmt"
26
+ "math/big"
22
27
"sync"
23
28
"testing"
24
29
"time"
@@ -49,6 +54,7 @@ import (
49
54
"sigs.k8s.io/cluster-api/feature"
50
55
"sigs.k8s.io/cluster-api/internal/test/builder"
51
56
"sigs.k8s.io/cluster-api/util"
57
+ "sigs.k8s.io/cluster-api/util/certs"
52
58
"sigs.k8s.io/cluster-api/util/collections"
53
59
"sigs.k8s.io/cluster-api/util/conditions"
54
60
"sigs.k8s.io/cluster-api/util/kubeconfig"
@@ -520,11 +526,12 @@ func TestKubeadmControlPlaneReconciler_adoption(t *testing.T) {
520
526
g .Expect (machine .GetAnnotations ()).NotTo (HaveKey (clusterv1 .TemplateClonedFromNameAnnotation ))
521
527
}
522
528
})
529
+
523
530
t .Run ("adopts v1alpha2 cluster secrets" , func (t * testing.T ) {
524
531
g := NewWithT (t )
525
532
526
533
cluster , kcp , tmpl := createClusterWithControlPlane (metav1 .NamespaceDefault )
527
- cluster .Spec .ControlPlaneEndpoint .Host = "bar "
534
+ cluster .Spec .ControlPlaneEndpoint .Host = "validhost "
528
535
cluster .Spec .ControlPlaneEndpoint .Port = 6443
529
536
cluster .Status .InfrastructureReady = true
530
537
kcp .Spec .Version = version
@@ -565,7 +572,7 @@ func TestKubeadmControlPlaneReconciler_adoption(t *testing.T) {
565
572
},
566
573
}
567
574
568
- // A simulcrum of the various Certificate and kubeconfig secrets
575
+ // A simulacrum of the various Certificate and kubeconfig secrets
569
576
// it's a little weird that this is one per KubeadmConfig rather than just whichever config was "first,"
570
577
// but the intent is to ensure that the owner is changed regardless of which Machine we start with
571
578
clusterSecret := & corev1.Secret {
@@ -749,6 +756,164 @@ func TestKubeadmControlPlaneReconciler_adoption(t *testing.T) {
749
756
})
750
757
}
751
758
759
+ func TestKubeadmControlPlaneReconciler_ensureOwnerReferences (t * testing.T ) {
760
+ g := NewWithT (t )
761
+
762
+ cluster , kcp , tmpl := createClusterWithControlPlane (metav1 .NamespaceDefault )
763
+ cluster .Spec .ControlPlaneEndpoint .Host = "bar"
764
+ cluster .Spec .ControlPlaneEndpoint .Port = 6443
765
+ cluster .Status .InfrastructureReady = true
766
+ kcp .Spec .Version = "v1.21.0"
767
+ key , err := certs .NewPrivateKey ()
768
+ g .Expect (err ).To (BeNil ())
769
+ crt , err := getTestCACert (key )
770
+ g .Expect (err ).To (BeNil ())
771
+
772
+ fmc := & fakeManagementCluster {
773
+ Machines : collections.Machines {},
774
+ Workload : fakeWorkloadCluster {},
775
+ }
776
+
777
+ clusterSecret := & corev1.Secret {
778
+ // The Secret's Type is used by KCP to determine whether it is user-provided.
779
+ // clusterv1.ClusterSecretType signals that the Secret is CAPI-provided.
780
+ ObjectMeta : metav1.ObjectMeta {
781
+ Namespace : cluster .Namespace ,
782
+ Name : "" ,
783
+ Labels : map [string ]string {
784
+ "cluster.x-k8s.io/cluster-name" : cluster .Name ,
785
+ "testing" : "yes" ,
786
+ },
787
+ },
788
+ Data : map [string ][]byte {
789
+ secret .TLSCrtDataName : certs .EncodeCertPEM (crt ),
790
+ secret .TLSKeyDataName : certs .EncodePrivateKeyPEM (key ),
791
+ },
792
+ }
793
+
794
+ t .Run ("add KCP owner for secrets with no controller reference" , func (t * testing.T ) {
795
+ objs := []client.Object {fakeGenericMachineTemplateCRD , cluster .DeepCopy (), kcp .DeepCopy (), tmpl .DeepCopy ()}
796
+ for _ , purpose := range []secret.Purpose {secret .ClusterCA , secret .FrontProxyCA , secret .ServiceAccount , secret .EtcdCA } {
797
+ s := clusterSecret .DeepCopy ()
798
+ // Set the secret name to the purpose
799
+ s .Name = secret .Name (cluster .Name , purpose )
800
+ // Set the Secret Type to clusterv1.ClusterSecretType which signals this Secret was generated by CAPI.
801
+ s .Type = clusterv1 .ClusterSecretType
802
+
803
+ objs = append (objs , s )
804
+ }
805
+
806
+ fakeClient := newFakeClient (objs ... )
807
+ fmc .Reader = fakeClient
808
+ r := & KubeadmControlPlaneReconciler {
809
+ Client : fakeClient ,
810
+ APIReader : fakeClient ,
811
+ managementCluster : fmc ,
812
+ managementClusterUncached : fmc ,
813
+ }
814
+
815
+ _ , err := r .reconcile (ctx , cluster , kcp )
816
+ g .Expect (err ).To (BeNil ())
817
+
818
+ secrets := & corev1.SecretList {}
819
+ g .Expect (fakeClient .List (ctx , secrets , client .InNamespace (cluster .Namespace ), client.MatchingLabels {"testing" : "yes" })).To (Succeed ())
820
+ for _ , secret := range secrets .Items {
821
+ g .Expect (secret .OwnerReferences ).To (ContainElement (* metav1 .NewControllerRef (kcp , controlplanev1 .GroupVersion .WithKind ("KubeadmControlPlane" ))))
822
+ }
823
+ })
824
+
825
+ t .Run ("replace non-KCP controller with KCP controller reference" , func (t * testing.T ) {
826
+ objs := []client.Object {fakeGenericMachineTemplateCRD , cluster .DeepCopy (), kcp .DeepCopy (), tmpl .DeepCopy ()}
827
+ // A simulacrum of the various Certificate and kubeconfig secrets
828
+ // it's a little weird that this is one per KubeadmConfig rather than just whichever config was "first,"
829
+ // but the intent is to ensure that the owner is changed regardless of which Machine we start with
830
+ for _ , purpose := range []secret.Purpose {secret .ClusterCA , secret .FrontProxyCA , secret .ServiceAccount , secret .EtcdCA } {
831
+ s := clusterSecret .DeepCopy ()
832
+ // Set the secret name to the purpose
833
+ s .Name = secret .Name (cluster .Name , purpose )
834
+ // Set the Secret Type to clusterv1.ClusterSecretType which signals this Secret was generated by CAPI.
835
+ s .Type = clusterv1 .ClusterSecretType
836
+
837
+ // Set the a controller owner reference of an unknown type on the secret.
838
+ s .SetOwnerReferences ([]metav1.OwnerReference {
839
+ {
840
+ APIVersion : bootstrapv1 .GroupVersion .String (),
841
+ // KCP should take ownership of any Secret of the correct type linked to the Cluster.
842
+ Kind : "OtherController" ,
843
+ Name : "name" ,
844
+ UID : "uid" ,
845
+ Controller : pointer .Bool (true ),
846
+ },
847
+ })
848
+ objs = append (objs , s )
849
+ }
850
+
851
+ fakeClient := newFakeClient (objs ... )
852
+ fmc .Reader = fakeClient
853
+ r := & KubeadmControlPlaneReconciler {
854
+ Client : fakeClient ,
855
+ APIReader : fakeClient ,
856
+ managementCluster : fmc ,
857
+ managementClusterUncached : fmc ,
858
+ }
859
+
860
+ _ , err := r .reconcile (ctx , cluster , kcp )
861
+ g .Expect (err ).To (BeNil ())
862
+
863
+ secrets := & corev1.SecretList {}
864
+ g .Expect (fakeClient .List (ctx , secrets , client .InNamespace (cluster .Namespace ), client.MatchingLabels {"testing" : "yes" })).To (Succeed ())
865
+ for _ , secret := range secrets .Items {
866
+ g .Expect (secret .OwnerReferences ).To (HaveLen (1 ))
867
+ g .Expect (secret .OwnerReferences ).To (ContainElement (* metav1 .NewControllerRef (kcp , controlplanev1 .GroupVersion .WithKind ("KubeadmControlPlane" ))))
868
+ }
869
+ })
870
+
871
+ t .Run ("does not add owner reference to user-provided secrets" , func (t * testing.T ) {
872
+ g := NewWithT (t )
873
+ objs := []client.Object {fakeGenericMachineTemplateCRD , cluster .DeepCopy (), kcp .DeepCopy (), tmpl .DeepCopy ()}
874
+ for _ , purpose := range []secret.Purpose {secret .ClusterCA , secret .FrontProxyCA , secret .ServiceAccount , secret .EtcdCA } {
875
+ s := clusterSecret .DeepCopy ()
876
+ // Set the secret name to the purpose
877
+ s .Name = secret .Name (cluster .Name , purpose )
878
+ // Set the Secret Type to any type which signals this Secret was is user-provided.
879
+ s .Type = corev1 .SecretTypeOpaque
880
+ // Set the a controller owner reference of an unknown type on the secret.
881
+ s .SetOwnerReferences ([]metav1.OwnerReference {
882
+ {
883
+ APIVersion : bootstrapv1 .GroupVersion .String (),
884
+ // This owner reference to a different controller should be preserved.
885
+ Kind : "OtherController" ,
886
+ Name : kcp .Name ,
887
+ UID : kcp .UID ,
888
+ Controller : pointer .Bool (true ),
889
+ BlockOwnerDeletion : pointer .Bool (true ),
890
+ },
891
+ })
892
+
893
+ objs = append (objs , s )
894
+ }
895
+
896
+ fakeClient := newFakeClient (objs ... )
897
+ fmc .Reader = fakeClient
898
+ r := & KubeadmControlPlaneReconciler {
899
+ Client : fakeClient ,
900
+ APIReader : fakeClient ,
901
+ managementCluster : fmc ,
902
+ managementClusterUncached : fmc ,
903
+ }
904
+
905
+ _ , err := r .reconcile (ctx , cluster , kcp )
906
+ g .Expect (err ).To (BeNil ())
907
+
908
+ secrets := & corev1.SecretList {}
909
+ g .Expect (fakeClient .List (ctx , secrets , client .InNamespace (cluster .Namespace ), client.MatchingLabels {"testing" : "yes" })).To (Succeed ())
910
+ for _ , secret := range secrets .Items {
911
+ g .Expect (secret .OwnerReferences ).To (HaveLen (1 ))
912
+ g .Expect (secret .OwnerReferences ).To (ContainElement (* metav1 .NewControllerRef (kcp , bootstrapv1 .GroupVersion .WithKind ("OtherController" ))))
913
+ }
914
+ })
915
+ }
916
+
752
917
func TestReconcileCertificateExpiries (t * testing.T ) {
753
918
g := NewWithT (t )
754
919
@@ -1769,3 +1934,34 @@ func newCluster(namespacedName *types.NamespacedName) *clusterv1.Cluster {
1769
1934
},
1770
1935
}
1771
1936
}
1937
+
1938
+ func getTestCACert (key * rsa.PrivateKey ) (* x509.Certificate , error ) {
1939
+ cfg := certs.Config {
1940
+ CommonName : "kubernetes" ,
1941
+ }
1942
+
1943
+ now := time .Now ().UTC ()
1944
+
1945
+ tmpl := x509.Certificate {
1946
+ SerialNumber : new (big.Int ).SetInt64 (0 ),
1947
+ Subject : pkix.Name {
1948
+ CommonName : cfg .CommonName ,
1949
+ Organization : cfg .Organization ,
1950
+ },
1951
+ NotBefore : now .Add (time .Minute * - 5 ),
1952
+ NotAfter : now .Add (time .Hour * 24 ), // 1 day
1953
+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature | x509 .KeyUsageCertSign ,
1954
+ MaxPathLenZero : true ,
1955
+ BasicConstraintsValid : true ,
1956
+ MaxPathLen : 0 ,
1957
+ IsCA : true ,
1958
+ }
1959
+
1960
+ b , err := x509 .CreateCertificate (rand .Reader , & tmpl , & tmpl , key .Public (), key )
1961
+ if err != nil {
1962
+ return nil , err
1963
+ }
1964
+
1965
+ c , err := x509 .ParseCertificate (b )
1966
+ return c , err
1967
+ }
0 commit comments