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 support to use public IP for the pod VM in Azure #2035

Closed
Closed
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
1 change: 1 addition & 0 deletions src/cloud-api-adaptor/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ azure() {
[[ "${AZURE_INSTANCE_SIZES}" ]] && optionals+="-instance-sizes ${AZURE_INSTANCE_SIZES} "
[[ "${TAGS}" ]] && optionals+="-tags ${TAGS} " # Custom tags applied to pod vm
[[ "${ENABLE_SECURE_BOOT}" == "true" ]] && optionals+="-enable-secure-boot "
[[ "${USE_PUBLIC_IP}" == "true" ]] && optionals+="-use-public-ip " # Use public IP for pod vm

set -x
exec cloud-api-adaptor azure \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ configMapGenerator:
#- AZURE_INSTANCE_SIZES="" # comma separated
#- TAGS="" # Uncomment and add key1=value1,key2=value2 etc if you want to use specific tags for podvm
#- FORWARDER_PORT="" # Uncomment and set if you want to use a specific port for agent-protocol-forwarder. Defaults to 15150
#- USE_PUBLIC_IP="true" # Uncomment if you want to use public ip for podvm
#- PEERPODS_LIMIT_PER_NODE="10" # Max number of peer pods that can be created per node. Default is 10
##TLS_SETTINGS
#- CACERT_FILE="/etc/certificates/ca.crt" # for TLS
Expand Down
3 changes: 2 additions & 1 deletion src/cloud-providers/azure/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ func (_ *Manager) ParseCmd(flags *flag.FlagSet) {
flags.StringVar(&azurecfg.SSHKeyPath, "ssh-key-path", "$HOME/.ssh/id_rsa.pub", "Path to SSH public key")
flags.StringVar(&azurecfg.SSHUserName, "ssh-username", "peerpod", "SSH User Name")
flags.BoolVar(&azurecfg.DisableCVM, "disable-cvm", false, "Use non-CVMs for peer pods")
// Add a List parameter to indicate differet type of instance sizes to be used for the Pod VMs
// Add a List parameter to indicate different type of instance sizes to be used for the Pod VMs
flags.Var(&azurecfg.InstanceSizes, "instance-sizes", "Instance sizes to be used for the Pod VMs, comma separated")
// Add a key value list parameter to indicate custom tags to be used for the Pod VMs
flags.Var(&azurecfg.Tags, "tags", "Custom tags (key=value pairs) to be used for the Pod VMs, comma separated")
flags.BoolVar(&azurecfg.EnableSecureBoot, "enable-secure-boot", false, "Enable secure boot for the VMs")
flags.BoolVar(&azurecfg.UsePublicIP, "use-public-ip", false, "Use Public IP for connecting to the kata-agent inside the Pod VM")
}

func (_ *Manager) LoadEnv() {
Expand Down
160 changes: 160 additions & 0 deletions src/cloud-providers/azure/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,110 @@ func (p *azureProvider) createNetworkInterface(ctx context.Context, nicName stri
return &resp.Interface, nil
}

// Method to update the network Interface with the public IP
func (p *azureProvider) attachPublicIpAddr(ctx context.Context, vmNic *armnetwork.Interface,
publicIpAddr *armnetwork.PublicIPAddress) error {
nicClient, err := armnetwork.NewInterfacesClient(p.serviceConfig.SubscriptionId, p.azureClient, nil)
if err != nil {
return fmt.Errorf("creating network interface client: %w", err)
}

// Update the network interface with the public IP
vmNic.Properties.IPConfigurations[0].Properties.PublicIPAddress = &armnetwork.PublicIPAddress{
ID: publicIpAddr.ID,
}

pollerResponse, err := nicClient.BeginCreateOrUpdate(ctx, p.serviceConfig.ResourceGroupName, *vmNic.Name, *vmNic, nil)
if err != nil {
return fmt.Errorf("beginning update of network interface: %w", err)
}

_, err = pollerResponse.PollUntilDone(ctx, nil)
if err != nil {
return fmt.Errorf("polling network interface update: %w", err)
}

return nil
}

// Method to create a public IP
func (p *azureProvider) createPublicIP(ctx context.Context, publicIPName string) (*armnetwork.PublicIPAddress, error) {
publicIPClient, err := armnetwork.NewPublicIPAddressesClient(p.serviceConfig.SubscriptionId, p.azureClient, nil)
if err != nil {
return nil, fmt.Errorf("creating public IP client: %w", err)
}

parameters := armnetwork.PublicIPAddress{
Name: to.Ptr(publicIPName),
Location: to.Ptr(p.serviceConfig.Region),
SKU: &armnetwork.PublicIPAddressSKU{
Name: to.Ptr(armnetwork.PublicIPAddressSKUNameBasic),
Tier: to.Ptr(armnetwork.PublicIPAddressSKUTierRegional),
},
Properties: &armnetwork.PublicIPAddressPropertiesFormat{
PublicIPAddressVersion: to.Ptr(armnetwork.IPVersionIPv4),
// Dynamic allocation method for the public IP is not working as expected
PublicIPAllocationMethod: to.Ptr(armnetwork.IPAllocationMethodStatic),
// Delete the public IP when the associated VM is deleted
// However for static allocation, the public IP is not deleted when the VM is deleted
// Need to delete it explicitly when the VM is deleted
DeleteOption: to.Ptr(armnetwork.DeleteOptionsDelete),
},

Tags: p.getResourceTags(),
}

pollerResponse, err := publicIPClient.BeginCreateOrUpdate(ctx, p.serviceConfig.ResourceGroupName, publicIPName, parameters, nil)
if err != nil {
return nil, fmt.Errorf("beginning creation or update of public IP: %w", err)
}

resp, err := pollerResponse.PollUntilDone(ctx, nil)
if err != nil {
return nil, fmt.Errorf("polling public IP creation: %w", err)
}

return &resp.PublicIPAddress, nil

}

// Method to delete the public IP
func (p *azureProvider) deletePublicIP(ctx context.Context, publicIpAddrName string) error {
publicIPClient, err := armnetwork.NewPublicIPAddressesClient(p.serviceConfig.SubscriptionId, p.azureClient, nil)
if err != nil {
return fmt.Errorf("creating public IP client: %w", err)
}

rg := p.serviceConfig.ResourceGroupName

// retry with exponential backoff
err = retry.Do(func() error {
pollerResponse, err := publicIPClient.BeginDelete(ctx, rg, publicIpAddrName, nil)
if err != nil {
return fmt.Errorf("beginning public IP deletion: %w", err)
}
_, err = pollerResponse.PollUntilDone(ctx, nil)
if err != nil {
return fmt.Errorf("waiting for public IP deletion: %w", err)
}
return nil
},
retry.Context(ctx),
retry.Attempts(4),
retry.Delay(180*time.Second),
retry.MaxDelay(180*time.Second),
retry.LastErrorOnly(true),
)

if err != nil {
logger.Printf("deleting network interface (%s): %s", publicIpAddrName, err)
return err
}

logger.Printf("successfully deleted nic (%s)", publicIpAddrName)
return nil
}

func (p *azureProvider) CreateInstance(ctx context.Context, podName, sandboxID string, cloudConfig cloudinit.CloudConfigGenerator, spec provider.InstanceTypeSpec) (*provider.Instance, error) {

instanceName := util.GenerateInstanceName(podName, sandboxID, maxInstanceNameLen)
Expand Down Expand Up @@ -189,6 +293,34 @@ func (p *azureProvider) CreateInstance(ctx context.Context, podName, sandboxID s
return nil, err
}

// Create public IP if serviceConfig.UsePublicIP is true
var publicIpAddr *armnetwork.PublicIPAddress
var publicIpName string

if p.serviceConfig.UsePublicIP {
publicIpName = fmt.Sprintf("%s-ipv4", instanceName)
publicIpAddr, err = p.createPublicIP(ctx, publicIpName)
if err != nil {
err = fmt.Errorf("creating public IP: %w", err)
logger.Printf("%v", err)
return nil, err
}

logger.Printf("public IP (%s) created with address: %s", *publicIpAddr.Name, *publicIpAddr.Properties.IPAddress)

// Attach the public IP to the NIC
err = p.attachPublicIpAddr(ctx, vmNIC, publicIpAddr)
if err != nil {
logger.Printf("error in attaching public IP to the NIC: %v", err)
// Delete the public IP if attaching fails
if err := p.deletePublicIP(ctx, publicIpName); err != nil {
logger.Printf("deleting public IP (%s): %s", publicIpName, err)
}
return nil, err
}

}

vmParameters, err := p.getVMParameters(instanceSize, diskName, cloudConfigData, sshBytes, instanceName, vmNIC)
if err != nil {
return nil, err
Expand All @@ -204,6 +336,13 @@ func (p *azureProvider) CreateInstance(ctx context.Context, podName, sandboxID s
if err := p.deleteNetworkInterface(context.Background(), nicName); err != nil {
logger.Printf("deleting nic async (%s): %s", nicName, err)
}

if p.serviceConfig.UsePublicIP {
if err := p.deletePublicIP(context.Background(), publicIpName); err != nil {
logger.Printf("deleting public IP (%s): %s", publicIpName, err)
}
}

return nil, fmt.Errorf("Creating instance (%v): %s", result, err)
}

Expand All @@ -215,6 +354,17 @@ func (p *azureProvider) CreateInstance(ctx context.Context, podName, sandboxID s
return nil, err
}

if p.serviceConfig.UsePublicIP && publicIpAddr != nil {
// Replace the first IP address with the public IP address
ip, err := netip.ParseAddr(*publicIpAddr.Properties.IPAddress)
if err != nil {
return nil, fmt.Errorf("parsing pod node public IP %q: %w", *publicIpAddr.Properties.IPAddress, err)
}
logger.Printf("publicIP=%s", ip.String())
ips[0] = ip

}

instance := &provider.Instance{
ID: instanceID,
Name: instanceName,
Expand Down Expand Up @@ -250,6 +400,16 @@ func (p *azureProvider) DeleteInstance(ctx context.Context, instanceID string) e
}

logger.Printf("deleted VM successfully: %s", vmName)

// Delete the public IP
publicIpAddrName := fmt.Sprintf("%s-ipv4", vmName)
err = p.deletePublicIP(ctx, publicIpAddrName)
if err != nil {
logger.Printf("deleting public IP (%s): %s", publicIpAddrName, err)
return err
}

logger.Printf("deleted public IP (%s) successfully", publicIpAddrName)
return nil
}

Expand Down
1 change: 1 addition & 0 deletions src/cloud-providers/azure/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type Config struct {
// Disabled by default, we want to do measured boot.
// Secure boot brings no additional security.
EnableSecureBoot bool
UsePublicIP bool
}

func (c Config) Redact() Config {
Expand Down
Loading