Skip to content

Commit a78270d

Browse files
authored
Feature/Support of Oracle Cloud Infrastructure(OCI) for KubeIP. (#161)
* added support for OCI * fix lint issues
1 parent d7f419c commit a78270d

21 files changed

+2845
-45
lines changed

README.md

+90-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Welcome to KubeIP v2, a complete overhaul of the popular [DoiT](https://www.doit
66
KubeIP [v1-main](https://github.com/doitintl/kubeip/tree/v1-main) open-source project, originally developed
77
by [Aviv Laufer](https://github.com/avivl).
88

9-
KubeIP v2 expands its support beyond Google Cloud (as in v1) to include AWS, and it's designed to be extendable to other cloud providers
9+
KubeIP v2 expands its support beyond Google Cloud (as in v1) to include AWS and Oracle Cloud Infrastructure(OCI), and it's designed to be extendable to other cloud providers
1010
that allow assigning static public IP to VMs. We've also transitioned from a Kubernetes controller to a standard DaemonSet, enhancing
1111
reliability and ease of use.
1212

@@ -252,6 +252,93 @@ To use this feature, add the `filter` flag (or set `FILTER` environment variable
252252
value: "labels.env=dev;labels.app=streamer"
253253
```
254254

255+
### Oracle Cloud Infrastructure (OCI)
256+
257+
Make sure that KubeIP DaemonSet is deployed on nodes that have a public IP (node running in public subnet). Set the [compartment OCID](https://docs.oracle.com/en-us/iaas/Content/GSG/Tasks/contactingsupport_topic-Locating_Oracle_Cloud_Infrastructure_IDs.htm#Finding_the_OCID_of_a_Compartment) in the `project` flag (or
258+
set `FILTER` environment variable) to the KubeIP DaemonSet:
259+
260+
```yaml
261+
- name: PROJECT
262+
value: "ocid1.compartment.oc1..test"
263+
```
264+
265+
KubeIP will also need certain permissions to communicate with the OCI APIs. Follow these steps to set up the necessary permissions and generate the required API key and place it in the KubeIP DaemonSet:
266+
267+
1. Create a [user and group](https://docs.oracle.com/en/cloud/paas/integration-cloud/oracle-integration-gov/create-iam-group.html) in the OCI console and add the following [policy](https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/managingpolicies.htm) to the group:
268+
269+
```
270+
Allow group <group_ocid> to manage public-ips in compartment id <compartment_ocid>
271+
Allow group <group_ocid> to manage private-ips in compartment id <compartment_ocid>
272+
Allow group <group_ocid> to manage vcns in compartment id <compartment_ocid>
273+
```
274+
275+
2. Generate an [API Key](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm#two) for the user and download the private key. Config file will look like this:
276+
277+
```
278+
[DEFAULT]
279+
user=ocid1.user.oc1..test
280+
fingerprint=
281+
key_file=/root/.oci/oci_api_key.pem
282+
tenancy=ocid1.tenancy.oc1..test
283+
region=us-ashburn-1
284+
```
285+
286+
3. Add the following [secret](https://kubernetes.io/docs/concepts/configuration/secret/) to the KubeIP DaemonSet:
287+
288+
```yaml
289+
apiVersion: v1
290+
kind: Secret
291+
metadata:
292+
name: kubeip-oci-secret
293+
namespace: kube-system
294+
type: Opaque
295+
data:
296+
config: <base64_encoded_oci_config>
297+
oci_api_key.pem: <base64_encoded_oci_api_key>
298+
```
299+
300+
4. Create a volume and mount in the KubeIP DaemonSet to mount the secret:
301+
302+
```yaml
303+
volumes:
304+
- name: oci-config
305+
secret:
306+
secretName: kubeip-oci-secret
307+
```
308+
309+
```yaml
310+
volumeMounts:
311+
- name: oci-config
312+
mountPath: /root/.oci
313+
```
314+
315+
5. Add the following environment variables to the KubeIP DaemonSet:
316+
```yaml
317+
- name: OCI_CONFIG_FILE
318+
value: /root/.oci/config
319+
```
320+
321+
KubeIP supports filtering of reserved Public IPs using tags. To use this feature, add the `filter` flag (or
322+
set `FILTER` environment variable) to the KubeIP DaemonSet:
323+
324+
```yaml
325+
- name: FILTER
326+
value: "freeformTags.env=dev"
327+
```
328+
329+
KubeIP OCI filter supports the following filter syntax:
330+
331+
- `freeformTags.<key>=<value>`
332+
333+
To specify multiple filters, separate them with a semicolon (`;`). For example:
334+
335+
```yaml
336+
- name: FILTER
337+
value: "freeformTags.env=dev;freeformTags.app=streamer"
338+
```
339+
340+
In the case of multiple filters, they are joined with an `AND`, and the request returns only results that match all the specified filters.
341+
255342
## How to contribute to KubeIP?
256343

257344
KubeIP is an open-source project, and we welcome your contributions!
@@ -287,8 +374,8 @@ OPTIONS:
287374
--kubeconfig value path to Kubernetes configuration file (not needed if running in node) [$KUBECONFIG]
288375
--node-name value Kubernetes node name (not needed if running in node) [$NODE_NAME]
289376
--order-by value order by for the IP addresses [$ORDER_BY]
290-
--project value name of the GCP project or the AWS account ID (not needed if running in node) [$PROJECT]
291-
--region value name of the GCP region or the AWS region (not needed if running in node) [$REGION]
377+
--project value name of the GCP project or the AWS account ID (not needed if running in node) or OCI compartment OCID (required for OCI) [$PROJECT]
378+
--region value name of the GCP region or the AWS region or the OCI region (not needed if running in node) [$REGION]
292379
--release-on-exit release the static public IP address on exit (default: true) [$RELEASE_ON_EXIT]
293380
--taint-key value specify a taint key to remove from the node once the static public IP address is assigned [$TAINT_KEY]
294381
--retry-attempts value number of attempts to assign the static public IP address (default: 10) [$RETRY_ATTEMPTS]

chart/templates/daemonset.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ spec:
4040
imagePullPolicy: Always
4141
resources:
4242
{{- toYaml .Values.daemonSet.resources | nindent 12 }}
43+
{{- if eq .Values.cloudProvider "oci" }}
44+
volumeMounts:
45+
- name: oci-config
46+
mountPath: /root/.oci
47+
{{- end }}
4348
env:
4449
- name: NODE_NAME
4550
valueFrom:
@@ -53,10 +58,20 @@ spec:
5358
value: {{ .Values.daemonSet.env.LOG_LEVEL | quote }}
5459
- name: LOG_JSON
5560
value: {{ .Values.daemonSet.env.LOG_JSON | quote }}
61+
{{- if eq .Values.cloudProvider "oci" }}
62+
- name: OCI_CONFIG_FILE
63+
value: /root/.oci/config
64+
{{- end }}
5665
securityContext:
5766
privileged: false
5867
allowPrivilegeEscalation: false
5968
capabilities:
6069
drop:
6170
- ALL
6271
readOnlyRootFilesystem: true
72+
{{- if eq .Values.cloudProvider "oci" }}
73+
volumes:
74+
- name: oci-config
75+
secret:
76+
secretName: oci-config
77+
{{- end }}

chart/templates/secrets.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{{- if .Values.secrets.create }}
2+
apiVersion: v1
3+
kind: Secret
4+
metadata:
5+
name: kubeip-oci-secret
6+
namespace: {{ .Values.namespaceOverride }}
7+
type: Opaque
8+
data:
9+
oci_config: {{ .Values.secrets.oci_config }}
10+
oci_oci_api_key.pem: {{ .Values.secrets.oci_oci_api_key }}
11+
{{- end }}

chart/values.yaml

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# The cloud provider where your Kubernetes cluster is running.
22
# This value determines the appropriate annotations for the Service Account.
3-
# Currently acceptable values are 'gcp' or 'aws'.
3+
# Currently acceptable values are 'gcp' or 'aws' or 'oci'.
44
cloudProvider: gcp
55

66
# The namespace where the kubeip-agent will be deployed.
@@ -27,6 +27,12 @@ rbac:
2727
create: true
2828
allowNodesPatchPermission: false
2929

30+
# Secret configuration for oci users.
31+
secrets:
32+
create: true
33+
oci_config: "" # base64 encoded oci config file
34+
oci_oci_api_key: "" # base64 encoded oci api key file
35+
3036
# DaemonSet configuration.
3137
daemonSet:
3238
terminationGracePeriodSeconds: 30

cmd/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -316,13 +316,13 @@ func main() {
316316
},
317317
&cli.StringFlag{
318318
Name: "project",
319-
Usage: "name of the GCP project or the AWS account ID (not needed if running in node)",
319+
Usage: "name of the GCP project or the AWS account ID (not needed if running in node) or OCI compartment OCID (required for OCI)",
320320
EnvVars: []string{"PROJECT"},
321321
Category: "Configuration",
322322
},
323323
&cli.StringFlag{
324324
Name: "region",
325-
Usage: "name of the GCP region or the AWS region (not needed if running in node)",
325+
Usage: "name of the GCP region or the AWS region or the OCI region (not needed if running in node)",
326326
EnvVars: []string{"REGION"},
327327
Category: "Configuration",
328328
},

go.mod

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/aws/aws-sdk-go-v2 v1.26.0
88
github.com/aws/aws-sdk-go-v2/config v1.27.9
99
github.com/aws/aws-sdk-go-v2/service/ec2 v1.152.0
10+
github.com/oracle/oci-go-sdk/v65 v65.80.0
1011
github.com/pkg/errors v0.9.1
1112
github.com/sirupsen/logrus v1.9.3
1213
github.com/stretchr/testify v1.9.0
@@ -15,6 +16,7 @@ require (
1516
k8s.io/api v0.29.3
1617
k8s.io/apimachinery v0.29.3
1718
k8s.io/client-go v0.29.3
19+
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
1820
sigs.k8s.io/controller-runtime v0.17.2
1921
)
2022

@@ -41,6 +43,7 @@ require (
4143
github.com/go-openapi/jsonpointer v0.21.0 // indirect
4244
github.com/go-openapi/jsonreference v0.21.0 // indirect
4345
github.com/go-openapi/swag v0.23.0 // indirect
46+
github.com/gofrs/flock v0.8.1 // indirect
4447
github.com/gogo/protobuf v1.3.2 // indirect
4548
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
4649
github.com/golang/protobuf v1.5.4 // indirect
@@ -60,6 +63,7 @@ require (
6063
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
6164
github.com/pmezard/go-difflib v1.0.0 // indirect
6265
github.com/russross/blackfriday/v2 v2.1.0 // indirect
66+
github.com/sony/gobreaker v0.5.0 // indirect
6367
github.com/spf13/pflag v1.0.5 // indirect
6468
github.com/stretchr/objx v0.5.2 // indirect
6569
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
@@ -85,7 +89,6 @@ require (
8589
gopkg.in/yaml.v3 v3.0.1 // indirect
8690
k8s.io/klog/v2 v2.120.1 // indirect
8791
k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 // indirect
88-
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
8992
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
9093
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
9194
sigs.k8s.io/yaml v1.4.0 // indirect

go.sum

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
6363
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
6464
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
6565
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
66+
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
67+
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
6668
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
6769
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
6870
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -138,6 +140,8 @@ github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY
138140
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
139141
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
140142
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
143+
github.com/oracle/oci-go-sdk/v65 v65.80.0 h1:Rr7QLMozd2DfDBKo6AB3DzLYQxAwuOG118+K5AAD5E8=
144+
github.com/oracle/oci-go-sdk/v65 v65.80.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
141145
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
142146
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
143147
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -149,6 +153,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
149153
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
150154
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
151155
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
156+
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
157+
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
152158
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
153159
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
154160
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -161,6 +167,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
161167
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
162168
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
163169
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
170+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
164171
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
165172
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
166173
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
@@ -226,6 +233,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
226233
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
227234
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
228235
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
236+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
229237
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
230238
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
231239
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

internal/address/assigner.go

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ func NewAssigner(ctx context.Context, logger *logrus.Entry, provider types.Cloud
2727
return &azureAssigner{}, nil
2828
} else if provider == types.CloudProviderGCP {
2929
return NewGCPAssigner(ctx, logger, cfg.Project, cfg.Region, cfg.IPv6)
30+
} else if provider == types.CloudProviderOCI {
31+
return NewOCIAssigner(ctx, logger, cfg)
3032
}
3133
return nil, ErrUnknownCloudProvider
3234
}

internal/address/aws.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func (a *awsAssigner) tryAssignAddress(ctx context.Context, address *types.Addre
236236

237237
func (a *awsAssigner) getNetworkInterfaceID(instance *types.Instance) (string, error) {
238238
// get network interface ID
239-
if instance.NetworkInterfaces == nil || len(instance.NetworkInterfaces) == 0 {
239+
if len(instance.NetworkInterfaces) == 0 {
240240
return "", errors.Errorf("no network interfaces found for instance %s", *instance.InstanceId)
241241
}
242242
// get primary network interface ID with public IP address (DeviceIndex == 0)

0 commit comments

Comments
 (0)