Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add create vnic details #205

Merged
merged 1 commit into from
Feb 3, 2023
Merged
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
2 changes: 1 addition & 1 deletion Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ if "default_registry" in settings:

tilt_helper_dockerfile_header = """
# Tilt image
FROM golang:1.17 as tilt-helper
FROM golang:1.18 as tilt-helper
# Support live reloading with Tilt
RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \
wget --output-document /start.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/start.sh && \
Expand Down
4 changes: 4 additions & 0 deletions api/v1beta1/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ const (
FailureDomainFailedReason = "FailureDomainFailedReconciliationFailed"
// InstanceLBBackendAdditionFailedReason used when addition to LB backend fails
InstanceLBBackendAdditionFailedReason = "BackendAdditionFailed"
// InstanceVnicAttachmentFailedReason used when attaching vnics to machine
InstanceVnicAttachmentFailedReason = "VnicAttachmentFailed"
// InstanceIPAddressNotFound used when IP address of the instance count not be found
InstanceIPAddressNotFound = "InstanceIPAddressNotFound"
// VcnEventReady used after reconciliation has completed successfully
Expand All @@ -91,6 +93,8 @@ const (
RouteTableEventReady = "RouteTableReady"
// SubnetEventReady used after reconciliation has completed successfully
SubnetEventReady = "SubnetReady"
// InstanceVnicAttachmentReady used after reconciliation has been completed successfully
InstanceVnicAttachmentReady = "VnicAttachmentReady"
// ApiServerLoadBalancerEventReady used after reconciliation has completed successfully
ApiServerLoadBalancerEventReady = "APIServerLoadBalancerReady"
// FailureDomainEventReady used after reconciliation has completed successfully
Expand Down
4 changes: 4 additions & 0 deletions api/v1beta1/ocimachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ type OCIMachineSpec struct {
// NetworkDetails defines the configuration options for the network
NetworkDetails NetworkDetails `json:"networkDetails,omitempty"`

// VnicAttachments defines the configuration options for the vnic(s) attached to the machine
// The network bandwidth and number of VNICs scale proportionately with the number of OCPUs.
VnicAttachments []VnicAttachment `json:"vnicAttachments,omitempty"`

// LaunchOptions defines the options for tuning the compatibility and performance of VM shapes
LaunchOptions *LaunchOptions `json:"launchOptions,omitempty"`

Expand Down
24 changes: 24 additions & 0 deletions api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,30 @@ type NetworkDetails struct {
AssignPrivateDnsRecord *bool `json:"assignPrivateDnsRecord,omitempty"`
}

type VnicAttachment struct {
// VnicAttachmentId defines the ID of the VnicAttachment
VnicAttachmentId *string `json:"vnicAttachmentId,omitempty"`

// AssignPublicIp defines whether the vnic should have a public IP address
// +optional
AssignPublicIp bool `json:"assignPublicIp,omitempty"`

// SubnetName defines the subnet name to use for the VNIC
// Defaults to the "worker" subnet if not provided
// +optional
SubnetName string `json:"subnetName,omitempty"`

// DisplayName defines a user-friendly name. Does not have to be unique.
// Avoid entering confidential information.
DisplayName *string `json:"displayName"`

// NicIndex defines which physical Network Interface Card (NIC) to use
// You can determine which NICs are active for a shape by reviewing the
// https://docs.oracle.com/en-us/iaas/Content/Compute/References/computeshapes.htm
// +optional
NicIndex *int `json:"nicIndex,omitempty"`
}

// LaunchOptionsBootVolumeTypeEnum Enum with underlying type: string
type LaunchOptionsBootVolumeTypeEnum string

Expand Down
37 changes: 37 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 17 additions & 13 deletions cloud/scope/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,7 @@ func (m *MachineScope) GetOrCreateMachine(ctx context.Context) (*core.Instance,

tags := m.getFreeFormTags(*m.OCICluster)

definedTags := make(map[string]map[string]interface{})
if m.OCIMachine.Spec.DefinedTags != nil {
for ns, mapNs := range m.OCIMachine.Spec.DefinedTags {
mapValues := make(map[string]interface{})
for k, v := range mapNs {
mapValues[k] = v
}
definedTags[ns] = mapValues
}
}
definedTags := ConvertMachineDefinedTags(m.OCIMachine.Spec.DefinedTags)

availabilityDomain := m.OCICluster.Status.FailureDomains[*failureDomain].Attributes[AvailabilityDomain]
faultDomain := m.OCICluster.Status.FailureDomains[*failureDomain].Attributes[FaultDomain]
Expand Down Expand Up @@ -309,8 +300,8 @@ func (m *MachineScope) DeleteMachine(ctx context.Context) error {

// IsResourceCreatedByClusterAPI determines if the instance was created by the cluster using the
// tags created at instance launch.
func (s *MachineScope) IsResourceCreatedByClusterAPI(resourceFreeFormTags map[string]string) bool {
tagsAddedByClusterAPI := ociutil.BuildClusterTags(string(s.OCICluster.GetOCIResourceIdentifier()))
func (m *MachineScope) IsResourceCreatedByClusterAPI(resourceFreeFormTags map[string]string) bool {
tagsAddedByClusterAPI := ociutil.BuildClusterTags(string(m.OCICluster.GetOCIResourceIdentifier()))
for k, v := range tagsAddedByClusterAPI {
if resourceFreeFormTags[k] != v {
return false
Expand Down Expand Up @@ -474,8 +465,10 @@ func (m *MachineScope) GetInstanceIp(ctx context.Context) (*string, error) {
}
}

if page = resp.OpcNextPage; resp.OpcNextPage == nil {
if resp.OpcNextPage == nil {
break
} else {
page = resp.OpcNextPage
}
}

Expand Down Expand Up @@ -620,6 +613,17 @@ func (m *MachineScope) getGetControlPlaneMachineNSGs() []string {
return nsgs
}

// getMachineSubnet iterates through the OCICluster Vcn subnets
// and returns the subnet ID if the name matches
func (m *MachineScope) getMachineSubnet(name string) (*string, error) {
for _, subnet := range m.OCICluster.Spec.NetworkSpec.Vcn.Subnets {
if subnet.Name == name {
return subnet.ID, nil
}
}
return nil, errors.New(fmt.Sprintf("Subnet with name %s not found for cluster %s", name, m.OCICluster.Name))
}

func (m *MachineScope) getWorkerMachineSubnet() *string {
for _, subnet := range m.OCICluster.Spec.NetworkSpec.Vcn.Subnets {
if subnet.Role == infrastructurev1beta1.WorkerRole {
Expand Down
17 changes: 17 additions & 0 deletions cloud/scope/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,20 @@ func GetSubnetNamesFromId(ids []string, subnets []*infrastructurev1beta1.Subnet)
}
return names
}

// ConvertMachineDefinedTags passes in the OCIMachineSpec DefinedTags and returns a converted map of defined tags
// to be used when creating API requests.
func ConvertMachineDefinedTags(machineDefinedTags map[string]map[string]string) map[string]map[string]interface{} {
definedTags := make(map[string]map[string]interface{})
if machineDefinedTags != nil {
for ns, mapNs := range machineDefinedTags {
mapValues := make(map[string]interface{})
for k, v := range mapNs {
mapValues[k] = v
}
definedTags[ns] = mapValues
}
}

return definedTags
}
129 changes: 129 additions & 0 deletions cloud/scope/vnic_reconciler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
Copyright (c) 2022 Oracle and/or its affiliates.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package scope
shyamradhakrishnan marked this conversation as resolved.
Show resolved Hide resolved
joekr marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"
"fmt"
"github.com/oracle/cluster-api-provider-oci/cloud/ociutil"

infrastructurev1beta1 "github.com/oracle/cluster-api-provider-oci/api/v1beta1"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/core"
"github.com/pkg/errors"
)

func (m *MachineScope) ReconcileVnicAttachments(ctx context.Context) error {
if m.IsControlPlane() {
return errors.New("cannot attach multiple vnics to ControlPlane machines")
}

for index, vnicAttachment := range m.OCIMachine.Spec.VnicAttachments {
if m.vnicAttachmentExists(ctx, vnicAttachment) {
shyamradhakrishnan marked this conversation as resolved.
Show resolved Hide resolved
m.Logger.Info("vnicAttachment", ociutil.DerefString(vnicAttachment.DisplayName), " already exists and is immutable")
continue
}

vnicId, err := m.createVnicAttachment(ctx, vnicAttachment)
if err != nil {
msg := fmt.Sprintf("Error creating VnicAttachment %s for cluster %s",
*vnicAttachment.DisplayName, m.Cluster.Name)
m.Logger.Error(err, msg)
return err
}

m.OCIMachine.Spec.VnicAttachments[index].VnicAttachmentId = vnicId
}

return nil
}

func (m *MachineScope) createVnicAttachment(ctx context.Context, spec infrastructurev1beta1.VnicAttachment) (*string, error) {
vnicName := spec.DisplayName

// Default to machine subnet if spec doesn't supply one
subnetId := m.getWorkerMachineSubnet()
if spec.SubnetName != "" {
var err error
subnetId, err = m.getMachineSubnet(spec.SubnetName)
if err != nil {
return nil, err
}
}

tags := m.getFreeFormTags(*m.OCICluster)

definedTags := ConvertMachineDefinedTags(m.OCIMachine.Spec.DefinedTags)

if spec.NicIndex == nil {
spec.NicIndex = common.Int(0)
}

secondVnic := core.AttachVnicDetails{
DisplayName: vnicName,
NicIndex: spec.NicIndex,
InstanceId: m.OCIMachine.Spec.InstanceId,
CreateVnicDetails: &core.CreateVnicDetails{
SubnetId: subnetId,
AssignPublicIp: common.Bool(spec.AssignPublicIp),
FreeformTags: tags,
DefinedTags: definedTags,
HostnameLabel: m.OCIMachine.Spec.NetworkDetails.HostnameLabel,
NsgIds: m.getWorkerMachineNSGs(),
SkipSourceDestCheck: m.OCIMachine.Spec.NetworkDetails.SkipSourceDestCheck,
AssignPrivateDnsRecord: m.OCIMachine.Spec.NetworkDetails.AssignPrivateDnsRecord,
DisplayName: vnicName,
},
}

req := core.AttachVnicRequest{AttachVnicDetails: secondVnic}
resp, err := m.ComputeClient.AttachVnic(ctx, req)
if err != nil {
return nil, err
}

return resp.Id, nil
}

func (m *MachineScope) vnicAttachmentExists(ctx context.Context, vnic infrastructurev1beta1.VnicAttachment) bool {

found := false
var page *string
for {
resp, err := m.ComputeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
InstanceId: m.GetInstanceId(),
CompartmentId: common.String(m.getCompartmentId()),
Page: page,
})
if err != nil {
return false
}
for _, attachment := range resp.Items {
if ociutil.DerefString(attachment.DisplayName) == ociutil.DerefString(vnic.DisplayName) {
m.Logger.Info("Vnic is already attached ", attachment)
return true
}
}

if resp.OpcNextPage == nil {
break
} else {
page = resp.OpcNextPage
}
}
return found
}
Loading