11package bootstrap
22
33import (
4+ "context"
5+ "fmt"
46 "io/fs"
57 "path"
68 "time"
79
8- v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
10+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
11+ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
12+ k8serrors "k8s.io/apimachinery/pkg/api/errors"
13+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+ "k8s.io/apimachinery/pkg/runtime/schema"
15+ "k8s.io/apimachinery/pkg/util/wait"
916 "k8s.io/apimachinery/pkg/util/yaml"
17+ "k8s.io/client-go/discovery"
1018 "k8s.io/client-go/rest"
11- "sigs. k8s.io/controller-runtime/pkg/envtest "
19+ "k8s.io/client-go/util/retry "
1220)
1321
1422const (
@@ -18,15 +26,21 @@ const (
1826)
1927
2028// CRD installs the CRDs in the filesystem into the kube cluster configured by the rest config.
29+ // Deprecated: Use CRDs instead.
2130func CRD (restConfig * rest.Config , crdFS fs.ReadDirFS , dir string ) error {
22- crds := make ([]* v1.CustomResourceDefinition , 0 )
31+ return CRDs (context .Background (), restConfig , crdFS , dir )
32+ }
33+
34+ // CRDs installs the CRDs in the filesystem into the kube cluster configured by the rest config.
35+ func CRDs (ctx context.Context , restConfig * rest.Config , crdFS fs.ReadDirFS , dir string ) error {
36+ crds := make ([]* apiextensionsv1.CustomResourceDefinition , 0 )
2337
2438 crdFiles , err := crdFS .ReadDir (dir )
2539 if err != nil {
2640 return err
2741 }
2842 for _ , crdFile := range crdFiles {
29- var crd v1 .CustomResourceDefinition
43+ var crd apiextensionsv1 .CustomResourceDefinition
3044 file , err := crdFS .Open (path .Join (dir , crdFile .Name ()))
3145 if err != nil {
3246 return err
@@ -37,11 +51,87 @@ func CRD(restConfig *rest.Config, crdFS fs.ReadDirFS, dir string) error {
3751 crds = append (crds , & crd )
3852 }
3953
40- _ , err = envtest .InstallCRDs (restConfig , envtest.CRDInstallOptions {
41- CRDs : crds ,
42- MaxTime : maxCRDInstallTime ,
43- PollInterval : crdInstallPollInterval ,
44- CleanUpAfterUse : false ,
45- })
54+ if err := createCRDs (ctx , restConfig , crds ); err != nil {
55+ return err
56+ }
57+
58+ if err := waitForDiscovery (ctx , restConfig , crds ); err != nil {
59+ return err
60+ }
61+
4662 return err
4763}
64+
65+ // createCRDs creates (or updates) CRDs in the cluster
66+ func createCRDs (ctx context.Context , config * rest.Config , crds []* apiextensionsv1.CustomResourceDefinition ) error {
67+ c , err := clientset .NewForConfig (config )
68+ if err != nil {
69+ return err
70+ }
71+ crdClient := c .ApiextensionsV1 ().CustomResourceDefinitions ()
72+ for _ , crd := range crds {
73+ crd := crd
74+ _ , err = crdClient .Get (ctx , crd .GetName (), metav1.GetOptions {})
75+ if k8serrors .IsNotFound (err ) {
76+ if _ , err := crdClient .Create (ctx , crd , metav1.CreateOptions {}); err != nil {
77+ return fmt .Errorf ("unable to create CRD %q: %w" , crd .Name , err )
78+ }
79+ continue
80+ }
81+ if err != nil {
82+ return fmt .Errorf ("failed when fetching CRD %q: %w" , crd .Name , err )
83+ }
84+
85+ if err := retry .RetryOnConflict (retry .DefaultBackoff , func () error {
86+ got , err := crdClient .Get (ctx , crd .Name , metav1.GetOptions {})
87+ if err != nil {
88+ return err
89+ }
90+ crd .SetResourceVersion (got .GetResourceVersion ())
91+ _ , err = crdClient .Update (ctx , crd , metav1.UpdateOptions {})
92+ return err
93+ }); err != nil {
94+ return err
95+ }
96+ }
97+ return nil
98+ }
99+
100+ func waitForDiscovery (ctx context.Context , config * rest.Config , crds []* apiextensionsv1.CustomResourceDefinition ) error {
101+ gvrs := map [schema.GroupVersionResource ]struct {}{}
102+ for _ , crd := range crds {
103+ for _ , version := range crd .Spec .Versions {
104+ if ! version .Served {
105+ continue
106+ }
107+ gvrs [schema.GroupVersionResource {
108+ Group : crd .Spec .Group ,
109+ Version : version .Name ,
110+ Resource : crd .Spec .Names .Plural ,
111+ }] = struct {}{}
112+ }
113+ }
114+ discoveryClient , err := discovery .NewDiscoveryClientForConfig (config )
115+ if err != nil {
116+ return err
117+ }
118+
119+ return wait .PollUntilContextTimeout (ctx , crdInstallPollInterval , maxCRDInstallTime , true , func (ctx context.Context ) (done bool , err error ) {
120+ _ , serverGVRs , err := discoveryClient .ServerGroupsAndResources ()
121+ if err != nil {
122+ return false , nil
123+ }
124+
125+ for _ , gv := range serverGVRs {
126+ for _ , r := range gv .APIResources {
127+ delete (gvrs , schema.GroupVersionResource {
128+ Group : r .Group ,
129+ Version : r .Version ,
130+ Resource : r .Name ,
131+ })
132+ }
133+ }
134+
135+ return len (gvrs ) == 0 , nil
136+ })
137+ }
0 commit comments