Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 56 additions & 58 deletions cmd/oci-csi-controller-driver/csioptions/csioptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,75 +26,73 @@ const (
fssAddressSuffix = "-fss.sock"
fssVolumeNameAppendedPrefix = "-fss"
CrossNamespaceVolumeDataSource = "CrossNamespaceVolumeDataSource"
VolumeAttributesClass = "VolumeAttributesClass"
VolumeAttributesClass = "VolumeAttributesClass"
)

// CSIOptions structure which contains flag values
type CSIOptions struct {
Master string
Kubeconfig string
CsiAddress string
Endpoint string
FssCsiAddress string
FssEndpoint string
VolumeNamePrefix string
FssVolumeNamePrefix string
VolumeNameUUIDLength int
ShowVersion bool
RetryIntervalStart time.Duration
RetryIntervalMax time.Duration
WorkerThreads uint
OperationTimeout time.Duration
EnableLeaderElection bool
LeaderElectionType string
LeaderElectionNamespace string
StrictTopology bool
Resync time.Duration
Timeout time.Duration
FeatureGates map[string]bool
FinalizerThreads uint
MetricsAddress string
MetricsPath string
ExtraCreateMetadata bool
ReconcileSync time.Duration
EnableResizer bool
GroupSnapshotNamePrefix string
Master string
Kubeconfig string
CsiAddress string
Endpoint string
FssCsiAddress string
FssEndpoint string
VolumeNamePrefix string
FssVolumeNamePrefix string
VolumeNameUUIDLength int
ShowVersion bool
RetryIntervalStart time.Duration
RetryIntervalMax time.Duration
WorkerThreads uint
OperationTimeout time.Duration
EnableLeaderElection bool
LeaderElectionType string
LeaderElectionNamespace string
StrictTopology bool
Resync time.Duration
Timeout time.Duration
FeatureGates map[string]bool
FinalizerThreads uint
MetricsAddress string
MetricsPath string
ExtraCreateMetadata bool
ReconcileSync time.Duration
EnableResizer bool
GroupSnapshotNamePrefix string
GroupSnapshotNameUUIDLength int

}

// NewCSIOptions initializes the flag
func NewCSIOptions() *CSIOptions {
csioptions := CSIOptions{
Master: *flag.String("master", "", "kube master"),
Kubeconfig: *flag.String("kubeconfig", "", "cluster kube config"),
CsiAddress: *flag.String("csi-address", "/run/csi/socket", "Address of the CSI BV driver socket."),
Endpoint: *flag.String("csi-endpoint", "unix://tmp/csi.sock", "CSI BV endpoint"),
FssCsiAddress: *flag.String("fss-csi-address", "/run/fss/socket", "Address of the CSI FSS driver socket."),
FssEndpoint: *flag.String("fss-csi-endpoint", "unix://tmp/csi-fss.sock", "CSI FSS endpoint"),
VolumeNamePrefix: *flag.String("csi-volume-name-prefix", "pvc", "Prefix to apply to the name of a created volume."),
FssVolumeNamePrefix: *flag.String("fss-csi-volume-name-prefix", "pvc", "Prefix to apply to the name of a volume created for FSS."),
VolumeNameUUIDLength: *flag.Int("csi-volume-name-uuid-length", -1, "Truncates generated UUID of a created volume to this length. Defaults behavior is to NOT truncate."),
ShowVersion: *flag.Bool("csi-version", false, "Show version."),
RetryIntervalStart: *flag.Duration("csi-retry-interval-start", time.Second, "Initial retry interval of failed provisioning or deletion. It doubles with each failure, up to retry-interval-max."),
RetryIntervalMax: *flag.Duration("csi-retry-interval-max", 5*time.Minute, "Maximum retry interval of failed provisioning or deletion."),
WorkerThreads: *flag.Uint("csi-worker-threads", 100, "Number of provisioner worker threads, in other words nr. of simultaneous CSI calls."),
OperationTimeout: *flag.Duration("csi-op-timeout", 10*time.Second, "Timeout for waiting for creation or deletion of a volume"),
EnableLeaderElection: *flag.Bool("csi-enable-leader-election", false, "Enables leader election. If leader election is enabled, additional RBAC rules are required. Please refer to the Kubernetes CSI documentation for instructions on setting up these RBAC rules."),
LeaderElectionType: *flag.String("csi-leader-election-type", "endpoints", "the type of leader election, options are 'endpoints' (default) or 'leases' (strongly recommended). The 'endpoints' option is deprecated in favor of 'leases'."),
LeaderElectionNamespace: *flag.String("csi-leader-election-namespace", "", "Namespace where the leader election resource lives. Defaults to the pod namespace if not set."),
StrictTopology: *flag.Bool("csi-strict-topology", false, "Passes only selected node topology to CreateVolume Request, unlike default behavior of passing aggregated cluster topologies that match with topology keys of the selected node."),
Resync: *flag.Duration("csi-resync", 10*time.Minute, "Resync interval of the controller."),
Timeout: *flag.Duration("csi-timeout", 15*time.Second, "Timeout for waiting for attaching or detaching the volume."),
FinalizerThreads: *flag.Uint("cloning-protection-threads", 1, "Number of simultaniously running threads, handling cloning finalizer removal"),
MetricsAddress: *flag.String("metrics-address", "", "The TCP network address where the prometheus metrics endpoint will listen (example: `:8080`). The default is empty string, which means metrics endpoint is disabled."),
MetricsPath: *flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`."),
ExtraCreateMetadata: *flag.Bool("extra-create-metadata", false, "If set, add pv/pvc metadata to plugin create requests as parameters."),
ReconcileSync: *flag.Duration("reconcile-sync", 1*time.Minute, "Resync interval of the VolumeAttachment reconciler."),
EnableResizer: *flag.Bool("csi-bv-expansion-enabled", false, "Enables go routine csi-resizer."),
GroupSnapshotNamePrefix: *flag.String("groupsnapshot-name-prefix", "groupsnapshot", "Prefix to apply to the name of a created group snapshot"),
Master: *flag.String("master", "", "kube master"),
Kubeconfig: *flag.String("kubeconfig", "", "cluster kube config"),
CsiAddress: *flag.String("csi-address", "/run/csi/socket", "Address of the CSI BV driver socket."),
Endpoint: *flag.String("csi-endpoint", "unix://tmp/csi.sock", "CSI BV endpoint"),
FssCsiAddress: *flag.String("fss-csi-address", "/run/fss/socket", "Address of the CSI FSS driver socket."),
FssEndpoint: *flag.String("fss-csi-endpoint", "unix://tmp/csi-fss.sock", "CSI FSS endpoint"),
VolumeNamePrefix: *flag.String("csi-volume-name-prefix", "pvc", "Prefix to apply to the name of a created volume."),
FssVolumeNamePrefix: *flag.String("fss-csi-volume-name-prefix", "pvc", "Prefix to apply to the name of a volume created for FSS."),
VolumeNameUUIDLength: *flag.Int("csi-volume-name-uuid-length", -1, "Truncates generated UUID of a created volume to this length. Defaults behavior is to NOT truncate."),
ShowVersion: *flag.Bool("csi-version", false, "Show version."),
RetryIntervalStart: *flag.Duration("csi-retry-interval-start", time.Second, "Initial retry interval of failed provisioning or deletion. It doubles with each failure, up to retry-interval-max."),
RetryIntervalMax: *flag.Duration("csi-retry-interval-max", 5*time.Minute, "Maximum retry interval of failed provisioning or deletion."),
WorkerThreads: *flag.Uint("csi-worker-threads", 100, "Number of provisioner worker threads, in other words nr. of simultaneous CSI calls."),
OperationTimeout: *flag.Duration("csi-op-timeout", 10*time.Second, "Timeout for waiting for creation or deletion of a volume"),
EnableLeaderElection: *flag.Bool("csi-enable-leader-election", false, "Enables leader election. If leader election is enabled, additional RBAC rules are required. Please refer to the Kubernetes CSI documentation for instructions on setting up these RBAC rules."),
LeaderElectionType: *flag.String("csi-leader-election-type", "endpoints", "the type of leader election, options are 'endpoints' (default) or 'leases' (strongly recommended). The 'endpoints' option is deprecated in favor of 'leases'."),
LeaderElectionNamespace: *flag.String("csi-leader-election-namespace", "", "Namespace where the leader election resource lives. Defaults to the pod namespace if not set."),
StrictTopology: *flag.Bool("csi-strict-topology", false, "Passes only selected node topology to CreateVolume Request, unlike default behavior of passing aggregated cluster topologies that match with topology keys of the selected node."),
Resync: *flag.Duration("csi-resync", 10*time.Minute, "Resync interval of the controller."),
Timeout: *flag.Duration("csi-timeout", 15*time.Second, "Timeout for waiting for attaching or detaching the volume."),
FinalizerThreads: *flag.Uint("cloning-protection-threads", 1, "Number of simultaniously running threads, handling cloning finalizer removal"),
MetricsAddress: *flag.String("metrics-address", "", "The TCP network address where the prometheus metrics endpoint will listen (example: `:8080`). The default is empty string, which means metrics endpoint is disabled."),
MetricsPath: *flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`."),
ExtraCreateMetadata: *flag.Bool("extra-create-metadata", false, "If set, add pv/pvc metadata to plugin create requests as parameters."),
ReconcileSync: *flag.Duration("reconcile-sync", 1*time.Minute, "Resync interval of the VolumeAttachment reconciler."),
EnableResizer: *flag.Bool("csi-bv-expansion-enabled", false, "Enables go routine csi-resizer."),
GroupSnapshotNamePrefix: *flag.String("groupsnapshot-name-prefix", "groupsnapshot", "Prefix to apply to the name of a created group snapshot"),
GroupSnapshotNameUUIDLength: *flag.Int("groupsnapshot-name-uuid-length", -1, "Length in characters for the generated uuid of a created group snapshot. Defaults behavior is to NOT truncate."),

}
return &csioptions
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/oci-csi-node-driver/nodedriver/nodedriver.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"go.uber.org/zap"
)

//RunNodeDriver main function to start node driver
// RunNodeDriver main function to start node driver
func RunNodeDriver(nodeOptions nodedriveroptions.NodeOptions, stopCh <-chan struct{}) error {
logger := logging.Logger().Sugar()
logger.Sync()
Expand Down
6 changes: 3 additions & 3 deletions cmd/oci-csi-node-driver/nodedriveroptions/nodecsioptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@

package nodedriveroptions

//NodeCSIOptions contains details about the flag
// NodeCSIOptions contains details about the flag
type NodeCSIOptions struct {
Endpoint string // Used for Block Volume CSI driver
NodeID string
LogLevel string
Master string
Kubeconfig string

EnableFssDriver bool
FssEndpoint string
EnableFssDriver bool
FssEndpoint string
LustreCsiAddress string
LustreKubeletRegistrationPath string
LustreEndpoint string
Expand Down
30 changes: 30 additions & 0 deletions pkg/cloudprovider/providers/oci/ccm.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
cloudprovider "k8s.io/cloud-provider"

providercfg "github.com/oracle/oci-cloud-controller-manager/pkg/cloudprovider/providers/oci/config"
"github.com/oracle/oci-cloud-controller-manager/pkg/cloudprovider/providers/oci/ipam"
"github.com/oracle/oci-cloud-controller-manager/pkg/metrics"
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/client"
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/instance/metadata"
Expand Down Expand Up @@ -192,6 +193,31 @@ func (cp *CloudProvider) Initialize(clientBuilder cloudprovider.ControllerClient

go nodeInfoController.Run(wait.NeverStop)

// Initialize IPAM controller if enabled
if cp.config.IPAM != nil && cp.config.IPAM.EnableIPAM {
cp.logger.Info("Initializing OCI IPAM controller")

ipamController := ipam.NewCloudAllocator(
cp.kubeclient,
cp.client,
nodeInformer,
cp.config.IPAM.NodeCIDRMaskSizeIPv4,
cp.config.IPAM.PodSubnetIDs,
cp.config.IPAM.AutoAttachPodVNIC,
cp.config.IPAM.PodVNICDisplayName,
cp.logger,
)

// Start IPAM controller with 2 workers
go func() {
if err := ipamController.Run(context.Background(), 2); err != nil {
cp.logger.With("error", err).Error("IPAM controller exited with error")
}
}()

cp.logger.Info("OCI IPAM controller started")
}

cp.logger.Info("Waiting for node informer cache to sync")
if !cache.WaitForCacheSync(wait.NeverStop, nodeInformer.Informer().HasSynced, serviceInformer.Informer().HasSynced) {
utilruntime.HandleError(fmt.Errorf("Timed out waiting for informers to sync"))
Expand Down Expand Up @@ -263,6 +289,10 @@ func (cp *CloudProvider) Clusters() (cloudprovider.Clusters, bool) {
// Routes returns a routes interface along with whether the interface is
// supported.
func (cp *CloudProvider) Routes() (cloudprovider.Routes, bool) {
if cp.config.IPAM != nil && cp.config.IPAM.EnableIPAM {
cp.logger.Debug("Routes interface is supported (IPAM enabled)")
return &routes{cp: cp}, true
}
return nil, false
}

Expand Down
101 changes: 94 additions & 7 deletions pkg/cloudprovider/providers/oci/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
package config

import (
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/instance/metadata"
"fmt"
"io"
"os"

"github.com/oracle/oci-cloud-controller-manager/pkg/oci/instance/metadata"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/common/auth"
"github.com/pkg/errors"
Expand All @@ -29,12 +31,13 @@ import (
// AuthConfig holds the configuration required for communicating with the OCI
// API.
type AuthConfig struct {
Region string `yaml:"region"`
TenancyID string `yaml:"tenancy"`
UserID string `yaml:"user"`
PrivateKey string `yaml:"key"`
Fingerprint string `yaml:"fingerprint"`
Passphrase string `yaml:"passphrase"`
Region string `yaml:"region"`
TenancyID string `yaml:"tenancy"`
UserID string `yaml:"user"`
PrivateKey string `yaml:"key"`
PrivateKeyFile string `yaml:"key_file"`
Fingerprint string `yaml:"fingerprint"`
Passphrase string `yaml:"passphrase"`

// Used by the flex driver for OCID expansion. This should be moved to top level
// as it doesn't strictly relate to OCI authentication.
Expand Down Expand Up @@ -129,6 +132,33 @@ type InitialTags struct {
Common *TagConfig `yaml:"common"`
}

// IPAMConfig holds the configuration options for IPAM (IP Address Management)
// support in the OCI CCM.
type IPAMConfig struct {
// EnableIPAM enables IPAM support in the CCM. When enabled, the CCM will
// allocate pod CIDRs to nodes and manage routing via secondary VNICs.
EnableIPAM bool `yaml:"enableIPAM"`

// PodSubnetIDs maps availability domain names to pod subnet OCIDs.
// Each node in an AD will have a secondary VNIC attached in the corresponding pod subnet.
// Example: {"zkJl:US-ASHBURN-AD-1": "ocid1.subnet.oc1.iad.aaa..."}
PodSubnetIDs map[string]string `yaml:"podSubnetIds"`

// NodeCIDRMaskSizeIPv4 is the mask size for IPv4 node CIDRs (default: 24).
// This determines how large each node's pod CIDR will be.
// For example, 24 means each node gets a /24 (256 IPs) from the pod subnet.
NodeCIDRMaskSizeIPv4 int `yaml:"nodeCIDRMaskSizeIPv4"`

// AutoAttachPodVNIC if true, CCM will automatically attach secondary VNICs
// to nodes in pod subnets. If false, assumes VNICs are pre-attached.
AutoAttachPodVNIC bool `yaml:"autoAttachPodVNIC"`

// PodVNICDisplayName is the display name pattern for pod VNICs (default: "pod-vnic").
// Used to identify pod VNICs when AutoAttachPodVNIC is enabled or when validating
// existing VNIC attachments.
PodVNICDisplayName string `yaml:"podVNICDisplayName"`
}

// Config holds the OCI cloud-provider config passed to Kubernetes components
// via the --cloud-config option.
type Config struct {
Expand All @@ -139,11 +169,17 @@ type Config struct {
Metrics *MetricsConfig `yaml:"metrics"`
// Tags to be added to managed LB and BV
Tags *InitialTags `yaml:"tags"`
// IPAM configuration for pod CIDR allocation and management
IPAM *IPAMConfig `yaml:"ipam"`

RegionKey string `yaml:"regionKey"`

// When set to true, clients will use an instance principal configuration provider and ignore auth fields.
UseInstancePrincipals bool `yaml:"useInstancePrincipals"`

// When set to true, clients will use OKE workload identity and ignore auth fields.
UseWorkloadIdentity bool `yaml:"useWorkloadIdentity"`

// CompartmentID is the OCID of the Compartment within which the cluster
// resides.
CompartmentID string `yaml:"compartment"`
Expand All @@ -170,12 +206,37 @@ func (c *LoadBalancerConfig) Complete() {
}
}

// Complete the IPAM config applying defaults / overrides.
func (c *IPAMConfig) Complete() {
if !c.EnableIPAM {
return
}
// Set default node CIDR mask size if not specified
if c.NodeCIDRMaskSizeIPv4 == 0 {
c.NodeCIDRMaskSizeIPv4 = 24 // Default to /24 per node (256 IPs)
}
// Set default pod VNIC display name if not specified
if len(c.PodVNICDisplayName) == 0 {
c.PodVNICDisplayName = "pod-vnic"
}
}

// Complete the authentication config applying defaults / overrides.
func (c *AuthConfig) Complete() {
if len(c.Passphrase) == 0 && len(c.PrivateKeyPassphrase) > 0 {
zap.S().Warn("cloud-provider config: auth.key_passphrase is DEPRECIATED and will be removed in a later release. Please set auth.passphrase instead.")
c.Passphrase = c.PrivateKeyPassphrase
}
// Resolve PrivateKeyFile into PrivateKey when PrivateKey is not set directly.
if c.PrivateKey == "" && c.PrivateKeyFile != "" {
keyBytes, err := os.ReadFile(c.PrivateKeyFile)
if err != nil {
zap.S().Errorf("cloud-provider config: failed to read private key file %q: %v", c.PrivateKeyFile, err)
} else {
c.PrivateKey = string(keyBytes)
c.PrivateKeyFile = ""
}
}
if c.Region == "" || c.CompartmentID == "" {
meta, err := c.metadataSvc.Get()
if err != nil {
Expand All @@ -197,6 +258,9 @@ func (c *Config) Complete() {
if c.LoadBalancer != nil {
c.LoadBalancer.Complete()
}
if c.IPAM != nil {
c.IPAM.Complete()
}
c.Auth.Complete()
// Ensure backwards compatibility fields are set correctly.
if len(c.CompartmentID) == 0 && len(c.Auth.CompartmentID) > 0 {
Expand Down Expand Up @@ -278,6 +342,29 @@ func NewConfigurationProvider(cfg *Config) (common.ConfigurationProvider, error)
return cp, nil
}

if cfg.UseWorkloadIdentity {
// OCI SDK requires specific, dynamic environment variables for workload identity.
if err := os.Setenv(auth.ResourcePrincipalVersionEnvVar, auth.ResourcePrincipalVersion2_2); err != nil {
return nil, fmt.Errorf("unable to set OCI SDK environment variable: %s: %w", auth.ResourcePrincipalVersionEnvVar, err)
}
if err := os.Setenv(auth.ResourcePrincipalRegionEnvVar, cfg.RegionKey); err != nil {
return nil, fmt.Errorf("unable to set OCI SDK environment variable: %s: %w", auth.ResourcePrincipalRegionEnvVar, err)
}
cp, err := auth.OkeWorkloadIdentityConfigurationProvider()
if err != nil {
return nil, fmt.Errorf("unable to load workload-identity auth method. %v", err)
}
return cp, nil
}

if ociConfigFile := os.Getenv("OCI_CONFIG_FILE"); ociConfigFile != "" {
cp, err := common.ConfigurationProviderFromFile(ociConfigFile, cfg.Auth.Passphrase)
if err != nil {
return nil, fmt.Errorf("failed to load OCI config from %s: %w", ociConfigFile, err)
}
return cp, nil
}

conf = common.NewRawConfigurationProvider(
cfg.Auth.TenancyID,
cfg.Auth.UserID,
Expand Down
Loading