-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathcloudprovider.go
More file actions
193 lines (167 loc) · 7.46 KB
/
Copy pathcloudprovider.go
File metadata and controls
193 lines (167 loc) · 7.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package cloudprovider
import (
"context"
"errors"
"fmt"
"net"
"os"
"path/filepath"
"sync"
configv1 "github.com/openshift/api/config/v1"
apifeatures "github.com/openshift/api/features"
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
)
var (
ErrNoNetworkInterface = errors.New("no retrievable network interface")
ErrAlreadyExistingIP = errors.New("the requested IP for assignment is already assigned")
ErrNonExistingIP = errors.New("the requested IP for removal is not assigned")
UnexpectedURIErrorString = "the URI is not expected"
)
const UserAgent = "cloud-network-config-controller"
func UnexpectedURIError(uri string) error {
return fmt.Errorf("%s: %s", UnexpectedURIErrorString, uri)
}
type CloudProviderIntf interface {
// initCredentials initializes the cloud API credentials by reading the
// secret data which has been mounted in cloudProviderSecretLocation. The
// mounted secret data in Kubernetes is generated following a one-to-one
// mapping between each .data field and a corresponding file. Hence
// .data.foo will generate a file foo in that location with the decoded
// secret data, similarity we would have a file bar if .data.bar was
// defined.
initCredentials() error
// AssignPrivateIP attempts to assigning the IP address provided to the VM
// instance corresponding to the corev1.Node provided on the cloud the
// cluster is deployed on. NOTE: this operation is only performed against
// the first network interface defined for the VM. It will return an
// ErrAlreadyExistingIP if the IP provided is already associated with the
// node, it's up to the caller to decide what to do with that.
AssignPrivateIP(ip net.IP, node *corev1.Node) error
// ReleasePrivateIP attempts to releasing the IP address provided from the
// VM instance corresponding to the corev1.Node provided on the cloud the
// cluster is deployed on. NOTE: this operation is only performed against
// the first network interface defined for the VM.
ReleasePrivateIP(ip net.IP, node *corev1.Node) error
// GetNodeEgressIPConfiguration retrieves the egress IP configuration for
// the node, following the convention the cloud uses. This means
// specifically that: the IP capacity can be either hard-coded and global
// for all instance types and IP families (GCP, Azure) or variable per
// instance and IP family (AWS), also: the interface is either keyed by name
// (GCP) or ID (Azure, AWS).
// The cpicIPs parameter is a set of IP addresses that are
// managed by CloudPrivateIPConfigs and should be excluded from capacity calculations.
GetNodeEgressIPConfiguration(node *corev1.Node, cpicIPs sets.Set[string]) ([]*NodeEgressIPConfiguration, error)
// CleanupNode removes any internal state associated with the node.
// This should be called when a node is deleted.
CleanupNode(nodeName string)
}
// CloudProviderWithMoveIntf is additional interface that can be added to cloud
// plugins that can benefit from a separate set of operations on IP address
// failover, instead of running ReleasePrivateIP followed by AssignPrivateIP.
type CloudProviderWithMoveIntf interface {
// MovePrivateIP is called instead of ReleasePrivateIP followed by
// AssignPrivateIP if plugin implements CloudProviderWithMoveIntf. It
// should effectively move IP address from nodeToDel to nodeToAdd, but not
// necessarily remove resources from the cloud. E.g. in case of OpenStack
// we don't want to delete the reservation Neutron port, but rather just
// manipulate allowedAddressPairs on the nodeToDel and nodeToAdd ports to
// move the IP from one node to another.
MovePrivateIP(ip net.IP, nodeToAdd *corev1.Node, nodeToDel *corev1.Node) error
}
// CloudProviderConfig is all the command-line options needed to initialize
// a cloud provider client.
type CloudProviderConfig struct {
PlatformType string // one of AWS, Azure, GCP
APIOverride string // override the API endpoint URL. Used by all platforms.
CredentialDir string // override the default credential directory
ConfigDir string // override the default config directory
Region string // region, only used by AWS
AWSCAOverride string
AzureEnvironment string // The azure "environment", which is a set of API endpoints
OpenStackMaxAllowedAddressPairs int
}
type CloudProvider struct {
CloudProviderIntf
cfg CloudProviderConfig
ctx context.Context
}
type ifAddr struct {
IPv4 string `json:"ipv4,omitempty"`
IPv6 string `json:"ipv6,omitempty"`
}
type capacity struct {
IPv4 *int `json:"ipv4,omitempty"`
IPv6 *int `json:"ipv6,omitempty"`
IP *int `json:"ip,omitempty"`
}
// NodeEgressIPConfiguration stores details - specific to each cloud - which are
// important for performing egress IP assignments by the network plugin.
// Specifically this is:
// - Interface - ID / Name, depending on the cloud's convention
// - IP address capacity for each node, where the capacity is either IP family
// agnostic or not.
// - Subnet information for the first network interface, IP family specific
type NodeEgressIPConfiguration struct {
Interface string `json:"interface"`
IFAddr ifAddr `json:"ifaddr"`
Capacity capacity `json:"capacity"`
}
// String implements the stringer interface for pointers to NodeEgressIPConfiguration. This is used for the unit tests
// as it simplifies printing of the actual values instead of returning the memory address that is being pointed to.
func (n *NodeEgressIPConfiguration) String() string {
return fmt.Sprintf("%v", *n)
}
func NewCloudProviderClient(cfg CloudProviderConfig,
platformStatus *configv1.PlatformStatus,
featureGates featuregates.FeatureGate) (CloudProviderIntf, error) {
var cloudProviderIntf CloudProviderIntf
// Initialize a separate context from the main context, rationale: cloud
// provider operations might take more time to run than any "API server" /
// "in-cluster" operations, hence: if the main program gets terminated we'd
// like to finish processing everything we are currently processing and
// update our store (the cloud provider) before terminating, thus we can't
// use the main context because it will be cancelled in such events.
cloudProviderCtx := context.Background()
cp := CloudProvider{
ctx: cloudProviderCtx,
cfg: cfg,
}
switch cfg.PlatformType {
case PlatformTypeAzure:
var azurePlatformStatus *configv1.AzurePlatformStatus
if platformStatus != nil && platformStatus.Type == PlatformTypeAzure {
azurePlatformStatus = platformStatus.Azure
}
cloudProviderIntf = &Azure{
CloudProvider: cp,
platformStatus: azurePlatformStatus,
nodeLockMap: make(map[string]*sync.Mutex),
azureWorkloadIdentityEnabled: featureGates.Enabled(apifeatures.FeatureGateAzureWorkloadIdentity),
}
case PlatformTypeAWS:
cloudProviderIntf = &AWS{
CloudProvider: cp,
}
case PlatformTypeGCP:
cloudProviderIntf = &GCP{
CloudProvider: cp,
nodeLockMap: make(map[string]*sync.Mutex),
}
case PlatformTypeOpenStack:
cloudProviderIntf = &OpenStack{
CloudProvider: cp,
}
default:
return nil, fmt.Errorf("unsupported cloud provider platform type: %s", cfg.PlatformType)
}
return cloudProviderIntf, cloudProviderIntf.initCredentials()
}
func (c *CloudProvider) readSecretData(secret string) (string, error) {
data, err := os.ReadFile(filepath.Join(c.cfg.CredentialDir, secret))
if err != nil {
return "", fmt.Errorf("unable to read secret data, err: %v", err)
}
return string(data), nil
}