sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/cluster/client.go (about) 1 /* 2 Copyright 2019 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 cluster 18 19 import ( 20 "context" 21 "time" 22 23 "github.com/pkg/errors" 24 "k8s.io/apimachinery/pkg/util/wait" 25 26 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" 27 "sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository" 28 yaml "sigs.k8s.io/cluster-api/cmd/clusterctl/client/yamlprocessor" 29 logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log" 30 ) 31 32 // Kubeconfig is a type that specifies inputs related to the actual 33 // kubeconfig. 34 type Kubeconfig struct { 35 // Path to the kubeconfig file 36 Path string 37 // Specify context within the kubeconfig file. If empty, cluster client 38 // will use the current context. 39 Context string 40 } 41 42 // Client is used to interact with a management cluster. 43 // A management cluster contains following categories of objects: 44 // - provider components (e.g. the CRDs, controllers, RBAC) 45 // - provider inventory items (e.g. the list of installed providers/versions) 46 // - provider objects (e.g. clusters, AWS clusters, machines etc.) 47 type Client interface { 48 // Kubeconfig returns the kubeconfig used to access to a management cluster. 49 Kubeconfig() Kubeconfig 50 51 // Proxy return the Proxy used for operating objects in the management cluster. 52 Proxy() Proxy 53 54 // CertManager returns a CertManagerClient that can be used for 55 // operating the cert-manager components in the cluster. 56 CertManager() CertManagerClient 57 58 // ProviderComponents returns a ComponentsClient object that can be used for 59 // operating provider components objects in the management cluster (e.g. the CRDs, controllers, RBAC). 60 ProviderComponents() ComponentsClient 61 62 // ProviderInventory returns a InventoryClient object that can be used for 63 // operating provider inventory stored in the management cluster (e.g. the list of installed providers/versions). 64 ProviderInventory() InventoryClient 65 66 // ProviderInstaller returns a ProviderInstaller that enforces consistency rules for provider installation, 67 // trying to prevent e.g. controllers fighting for objects, inconsistent versions, etc. 68 ProviderInstaller() ProviderInstaller 69 70 // ObjectMover returns an ObjectMover that implements support for moving Cluster API objects (e.g. clusters, AWS clusters, machines, etc.). 71 // from one management cluster to another management cluster. 72 ObjectMover() ObjectMover 73 74 // ProviderUpgrader returns a ProviderUpgrader that supports upgrading Cluster API providers. 75 ProviderUpgrader() ProviderUpgrader 76 77 // Template has methods to work with templates stored in the cluster. 78 Template() TemplateClient 79 80 // WorkloadCluster has methods for fetching kubeconfig of workload cluster from management cluster. 81 WorkloadCluster() WorkloadCluster 82 83 // Topology returns a TopologyClient that can be used for performing dry run executions of the topology reconciler. 84 Topology() TopologyClient 85 } 86 87 // PollImmediateWaiter tries a condition func until it returns true, an error, or the timeout is reached. 88 type PollImmediateWaiter func(ctx context.Context, interval, timeout time.Duration, condition wait.ConditionWithContextFunc) error 89 90 // clusterClient implements Client. 91 type clusterClient struct { 92 configClient config.Client 93 kubeconfig Kubeconfig 94 proxy Proxy 95 repositoryClientFactory RepositoryClientFactory 96 pollImmediateWaiter PollImmediateWaiter 97 processor yaml.Processor 98 } 99 100 // RepositoryClientFactory defines a function that returns a new repository.Client. 101 type RepositoryClientFactory func(ctx context.Context, provider config.Provider, configClient config.Client, options ...repository.Option) (repository.Client, error) 102 103 // ensure clusterClient implements Client. 104 var _ Client = &clusterClient{} 105 106 func (c *clusterClient) Kubeconfig() Kubeconfig { 107 return c.kubeconfig 108 } 109 110 func (c *clusterClient) Proxy() Proxy { 111 return c.proxy 112 } 113 114 func (c *clusterClient) CertManager() CertManagerClient { 115 return newCertManagerClient(c.configClient, c.repositoryClientFactory, c.proxy, c.pollImmediateWaiter) 116 } 117 118 func (c *clusterClient) ProviderComponents() ComponentsClient { 119 return newComponentsClient(c.proxy) 120 } 121 122 func (c *clusterClient) ProviderInventory() InventoryClient { 123 return newInventoryClient(c.proxy, c.pollImmediateWaiter) 124 } 125 126 func (c *clusterClient) ProviderInstaller() ProviderInstaller { 127 return newProviderInstaller(c.configClient, c.repositoryClientFactory, c.proxy, c.ProviderInventory(), c.ProviderComponents()) 128 } 129 130 func (c *clusterClient) ObjectMover() ObjectMover { 131 return newObjectMover(c.proxy, c.ProviderInventory()) 132 } 133 134 func (c *clusterClient) ProviderUpgrader() ProviderUpgrader { 135 return newProviderUpgrader(c.configClient, c.proxy, c.repositoryClientFactory, c.ProviderInventory(), c.ProviderComponents()) 136 } 137 138 func (c *clusterClient) Template() TemplateClient { 139 return newTemplateClient(TemplateClientInput{c.proxy, c.configClient, c.processor}) 140 } 141 142 func (c *clusterClient) WorkloadCluster() WorkloadCluster { 143 return newWorkloadCluster(c.proxy) 144 } 145 146 func (c *clusterClient) Topology() TopologyClient { 147 return newTopologyClient(c.proxy, c.ProviderInventory()) 148 } 149 150 // Option is a configuration option supplied to New. 151 type Option func(*clusterClient) 152 153 // InjectProxy allows to override the default proxy used by clusterctl. 154 func InjectProxy(proxy Proxy) Option { 155 return func(c *clusterClient) { 156 c.proxy = proxy 157 } 158 } 159 160 // InjectRepositoryFactory allows to override the default factory used for creating 161 // RepositoryClient objects. 162 func InjectRepositoryFactory(factory RepositoryClientFactory) Option { 163 return func(c *clusterClient) { 164 c.repositoryClientFactory = factory 165 } 166 } 167 168 // InjectPollImmediateWaiter allows to override the default PollImmediateWaiter used by clusterctl. 169 func InjectPollImmediateWaiter(pollImmediateWaiter PollImmediateWaiter) Option { 170 return func(c *clusterClient) { 171 c.pollImmediateWaiter = pollImmediateWaiter 172 } 173 } 174 175 // InjectYamlProcessor allows you to override the yaml processor that the 176 // cluster client uses. By default, the SimpleProcessor is used. This is 177 // true even if a nil processor is injected. 178 func InjectYamlProcessor(p yaml.Processor) Option { 179 return func(c *clusterClient) { 180 if p != nil { 181 c.processor = p 182 } 183 } 184 } 185 186 // New returns a cluster.Client. 187 func New(kubeconfig Kubeconfig, configClient config.Client, options ...Option) Client { 188 return newClusterClient(kubeconfig, configClient, options...) 189 } 190 191 func newClusterClient(kubeconfig Kubeconfig, configClient config.Client, options ...Option) *clusterClient { 192 client := &clusterClient{ 193 configClient: configClient, 194 kubeconfig: kubeconfig, 195 processor: yaml.NewSimpleProcessor(), 196 } 197 for _, o := range options { 198 o(client) 199 } 200 201 // if there is an injected proxy, use it, otherwise use a default one 202 if client.proxy == nil { 203 client.proxy = newProxy(client.kubeconfig) 204 } 205 206 // if there is an injected repositoryClientFactory, use it, otherwise use the default one 207 if client.repositoryClientFactory == nil { 208 client.repositoryClientFactory = repository.New 209 } 210 211 // if there is an injected PollImmediateWaiter, use it, otherwise use the default one 212 if client.pollImmediateWaiter == nil { 213 client.pollImmediateWaiter = func(ctx context.Context, interval, timeout time.Duration, condition wait.ConditionWithContextFunc) error { 214 return wait.PollUntilContextTimeout(ctx, interval, timeout, true, condition) 215 } 216 } 217 218 return client 219 } 220 221 // retryWithExponentialBackoff repeats an operation until it passes or the exponential backoff times out. 222 func retryWithExponentialBackoff(ctx context.Context, opts wait.Backoff, operation func(ctx context.Context) error) error { 223 log := logf.Log 224 225 i := 0 226 err := wait.ExponentialBackoffWithContext(ctx, opts, func(ctx context.Context) (bool, error) { 227 i++ 228 if err := operation(ctx); err != nil { 229 if i < opts.Steps { 230 log.V(5).Info("Retrying with backoff", "Cause", err.Error()) 231 return false, nil 232 } 233 return false, err 234 } 235 return true, nil 236 }) 237 if err != nil { 238 return errors.Wrapf(err, "action failed after %d attempts", i) 239 } 240 return nil 241 } 242 243 // newWriteBackoff creates a new API Machinery backoff parameter set suitable for use with clusterctl write operations. 244 func newWriteBackoff() wait.Backoff { 245 // Return a exponential backoff configuration which returns durations for a total time of ~40s. 246 // Example: 0, .5s, 1.2s, 2.3s, 4s, 6s, 10s, 16s, 24s, 37s 247 // Jitter is added as a random fraction of the duration multiplied by the jitter factor. 248 return wait.Backoff{ 249 Duration: 500 * time.Millisecond, 250 Factor: 1.5, 251 Steps: 10, 252 Jitter: 0.4, 253 } 254 } 255 256 // newConnectBackoff creates a new API Machinery backoff parameter set suitable for use when clusterctl connect to a cluster. 257 func newConnectBackoff() wait.Backoff { 258 // Return a exponential backoff configuration which returns durations for a total time of ~15s. 259 // Example: 0, .25s, .6s, 1.2, 2.1s, 3.4s, 5.5s, 8s, 12s 260 // Jitter is added as a random fraction of the duration multiplied by the jitter factor. 261 return wait.Backoff{ 262 Duration: 250 * time.Millisecond, 263 Factor: 1.5, 264 Steps: 9, 265 Jitter: 0.1, 266 } 267 } 268 269 // newShortConnectBackoff creates a new API Machinery backoff parameter set suitable for use when clusterctl connect to a cluster. 270 // Preferred over newConnectBackoff() only when used to perform quick checks to check if a cluster is reachable. 271 func newShortConnectBackoff() wait.Backoff { 272 // Return a exponential backoff configuration which returns durations for a total time of ~5s. 273 // Example: 0, .25s, .6s, 1.2, 2.1s, 3.4s, 5.5s. 274 // Jitter is added as a random fraction of the duration multiplied by the jitter factor. 275 return wait.Backoff{ 276 Duration: 250 * time.Millisecond, 277 Factor: 1.5, 278 Steps: 7, 279 Jitter: 0.1, 280 } 281 } 282 283 // newReadBackoff creates a new API Machinery backoff parameter set suitable for use with clusterctl read operations. 284 func newReadBackoff() wait.Backoff { 285 // Return a exponential backoff configuration which returns durations for a total time of ~15s. 286 // Example: 0, .25s, .6s, 1.2, 2.1s, 3.4s, 5.5s, 8s, 12s 287 // Jitter is added as a random fraction of the duration multiplied by the jitter factor. 288 return wait.Backoff{ 289 Duration: 250 * time.Millisecond, 290 Factor: 1.5, 291 Steps: 9, 292 Jitter: 0.1, 293 } 294 }