sigs.k8s.io/cluster-api@v1.7.1/internal/contract/infrastructure_cluster.go (about) 1 /* 2 Copyright 2021 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 contract 18 19 import ( 20 "encoding/json" 21 "strings" 22 "sync" 23 24 "github.com/pkg/errors" 25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 26 27 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 28 ) 29 30 // InfrastructureClusterContract encodes information about the Cluster API contract for InfrastructureCluster objects 31 // like DockerClusters, AWS Clusters, etc. 32 type InfrastructureClusterContract struct{} 33 34 var infrastructureCluster *InfrastructureClusterContract 35 var onceInfrastructureCluster sync.Once 36 37 // InfrastructureCluster provide access to the information about the Cluster API contract for InfrastructureCluster objects. 38 func InfrastructureCluster() *InfrastructureClusterContract { 39 onceInfrastructureCluster.Do(func() { 40 infrastructureCluster = &InfrastructureClusterContract{} 41 }) 42 return infrastructureCluster 43 } 44 45 // ControlPlaneEndpoint provides access to ControlPlaneEndpoint in an InfrastructureCluster object. 46 func (c *InfrastructureClusterContract) ControlPlaneEndpoint() *InfrastructureClusterControlPlaneEndpoint { 47 return &InfrastructureClusterControlPlaneEndpoint{} 48 } 49 50 // InfrastructureClusterControlPlaneEndpoint provides a helper struct for working with ControlPlaneEndpoint 51 // in an InfrastructureCluster object. 52 type InfrastructureClusterControlPlaneEndpoint struct{} 53 54 // Host provides access to the host field in the ControlPlaneEndpoint in an InfrastructureCluster object. 55 func (c *InfrastructureClusterControlPlaneEndpoint) Host() *String { 56 return &String{ 57 path: []string{"spec", "controlPlaneEndpoint", "host"}, 58 } 59 } 60 61 // Port provides access to the port field in the ControlPlaneEndpoint in an InfrastructureCluster object. 62 func (c *InfrastructureClusterControlPlaneEndpoint) Port() *Int64 { 63 return &Int64{ 64 path: []string{"spec", "controlPlaneEndpoint", "port"}, 65 } 66 } 67 68 // Ready provides access to the status.ready field in an InfrastructureCluster object. 69 func (c *InfrastructureClusterContract) Ready() *Bool { 70 return &Bool{ 71 path: []string{"status", "ready"}, 72 } 73 } 74 75 // FailureReason provides access to the status.failureReason field in an InfrastructureCluster object. Note that this field is optional. 76 func (c *InfrastructureClusterContract) FailureReason() *String { 77 return &String{ 78 path: []string{"status", "failureReason"}, 79 } 80 } 81 82 // FailureMessage provides access to the status.failureMessage field in an InfrastructureCluster object. Note that this field is optional. 83 func (c *InfrastructureClusterContract) FailureMessage() *String { 84 return &String{ 85 path: []string{"status", "failureMessage"}, 86 } 87 } 88 89 // FailureDomains provides access to the status.failureDomains field in an InfrastructureCluster object. Note that this field is optional. 90 func (c *InfrastructureClusterContract) FailureDomains() *FailureDomains { 91 return &FailureDomains{ 92 path: []string{"status", "failureDomains"}, 93 } 94 } 95 96 // IgnorePaths returns a list of paths to be ignored when reconciling an InfrastructureCluster. 97 // NOTE: The controlPlaneEndpoint struct currently contains two mandatory fields (host and port). 98 // As the host and port fields are not using omitempty, they are automatically set to their zero values 99 // if they are not set by the user. We don't want to reconcile the zero values as we would then overwrite 100 // changes applied by the infrastructure provider controller. 101 func (c *InfrastructureClusterContract) IgnorePaths(infrastructureCluster *unstructured.Unstructured) ([]Path, error) { 102 var ignorePaths []Path 103 104 host, ok, err := unstructured.NestedString(infrastructureCluster.UnstructuredContent(), InfrastructureCluster().ControlPlaneEndpoint().Host().Path()...) 105 if err != nil { 106 return nil, errors.Wrapf(err, "failed to retrieve %s", InfrastructureCluster().ControlPlaneEndpoint().Host().Path().String()) 107 } 108 if ok && host == "" { 109 ignorePaths = append(ignorePaths, InfrastructureCluster().ControlPlaneEndpoint().Host().Path()) 110 } 111 112 port, ok, err := unstructured.NestedInt64(infrastructureCluster.UnstructuredContent(), InfrastructureCluster().ControlPlaneEndpoint().Port().Path()...) 113 if err != nil { 114 return nil, errors.Wrapf(err, "failed to retrieve %s", InfrastructureCluster().ControlPlaneEndpoint().Port().Path().String()) 115 } 116 if ok && port == 0 { 117 ignorePaths = append(ignorePaths, InfrastructureCluster().ControlPlaneEndpoint().Port().Path()) 118 } 119 120 return ignorePaths, nil 121 } 122 123 // FailureDomains represents an accessor to a clusterv1.FailureDomains path value. 124 type FailureDomains struct { 125 path Path 126 } 127 128 // Path returns the path to the clusterv1.FailureDomains value. 129 func (d *FailureDomains) Path() Path { 130 return d.path 131 } 132 133 // Get gets the metav1.MachineAddressList value. 134 func (d *FailureDomains) Get(obj *unstructured.Unstructured) (*clusterv1.FailureDomains, error) { 135 domainMap, ok, err := unstructured.NestedMap(obj.UnstructuredContent(), d.path...) 136 if err != nil { 137 return nil, errors.Wrapf(err, "failed to get %s from object", "."+strings.Join(d.path, ".")) 138 } 139 if !ok { 140 return nil, errors.Wrapf(ErrFieldNotFound, "path %s", "."+strings.Join(d.path, ".")) 141 } 142 143 domains := make(clusterv1.FailureDomains, len(domainMap)) 144 s, err := json.Marshal(domainMap) 145 if err != nil { 146 return nil, errors.Wrapf(err, "failed to marshall field at %s to json", "."+strings.Join(d.path, ".")) 147 } 148 err = json.Unmarshal(s, &domains) 149 if err != nil { 150 return nil, errors.Wrapf(err, "failed to unmarshall field at %s to json", "."+strings.Join(d.path, ".")) 151 } 152 153 return &domains, nil 154 } 155 156 // Set sets the clusterv1.FailureDomains value in the path. 157 func (d *FailureDomains) Set(obj *unstructured.Unstructured, values clusterv1.FailureDomains) error { 158 domains := make(map[string]interface{}, len(values)) 159 s, err := json.Marshal(values) 160 if err != nil { 161 return errors.Wrapf(err, "failed to marshall supplied values to json for path %s", "."+strings.Join(d.path, ".")) 162 } 163 err = json.Unmarshal(s, &domains) 164 if err != nil { 165 return errors.Wrapf(err, "failed to unmarshall supplied values to json for path %s", "."+strings.Join(d.path, ".")) 166 } 167 168 if err := unstructured.SetNestedField(obj.UnstructuredContent(), domains, d.path...); err != nil { 169 return errors.Wrapf(err, "failed to set path %s of object %v", "."+strings.Join(d.path, "."), obj.GroupVersionKind()) 170 } 171 return nil 172 }