-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #307 from akutz/fix/vapp-doc-issues
🐛 Fix bugs in vApp bootstrap docs
- Loading branch information
Showing
4 changed files
with
65 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,89 @@ | ||
# Deploy a VM with vAppConfig | ||
|
||
This page reviews the bootstrap method vAppConfig. | ||
The vAppConfig bootstrap method is useful for legacy VM images that rely on bespoke, boot-time processes that leverage vAppConfig properties for customizing a guest. This method also supports properties specified with Golang-style template strings in order to use information not known ahead of time, such as the networking configuration, into the guest via vApp properties. | ||
|
||
## Description | ||
The vAppConfig bootstrap method is useful for legacy VM images that rely on bespoke, boot-time processes that leverage vAppConfig properties for customizing a guest. | ||
## Example | ||
|
||
## Purpose | ||
As a DevOps user, you can assign Golang-based template strings to vAppConfig values to send information into the guest that is not known ahead of time, such as the IPAM data settings used to configure the VM's network stack. | ||
The following example showcases a `VirtualMachine` resource that specifies one or more vApp properties used to bootstrap a guest. | ||
|
||
## Example | ||
=== "VirtualMachine" | ||
|
||
```yaml | ||
apiVersion: vmoperator.vmware.com/v1alpha1 | ||
kind: VirtualMachine | ||
metadata: | ||
name: legacy-vm | ||
namespace: test-ns | ||
name: legacy-vm | ||
namespace: test-ns | ||
spec: | ||
className: best-effort-small | ||
imageName: haproxy-v0.2.0 | ||
powerState: poweredOn | ||
storageClass: wcpglobal-storage-profile | ||
vmMetadata: | ||
className: best-effort-small | ||
imageName: haproxy-v0.2.0 | ||
powerState: poweredOn | ||
storageClass: wcpglobal-storage-profile | ||
vmMetadata: | ||
secretName: my-secret | ||
transport: vAppConfig | ||
``` | ||
|
||
=== "vAppConfig" | ||
|
||
!!! note "The vApp properties below..." | ||
|
||
The vApp properties used in this example are not standard. All images may define their own properties for configuring the guest's network, or anything else. Please review the image's OVF to understand what properties are available and should be assigned. | ||
|
||
```yaml | ||
apiVersion: v1 | ||
kind: Secret | ||
metadata: | ||
name: my-secret | ||
namespace: test-ns | ||
name: my-secret | ||
namespace: test-ns | ||
stringData: | ||
nameservers: "{{ (index .V1alpha1.Net.Nameservers 0) }}" | ||
management_ip: "{{ (index (index .V1alpha1.Net.Devices 0).IPAddresses 0) }}" | ||
hostname: "{{ .V1alpha1.VM.Name }} " | ||
management_gateway: "{{ (index .V1alpha1.Net.Devices 0).Gateway4 }}" | ||
nameservers: "{{ (index .V1alpha1.Net.Nameservers 0) }}" | ||
management_ip: "{{ (index (index .V1alpha1.Net.Devices 0).IPAddresses 0) }}" | ||
hostname: "{{ .V1alpha1.VM.Name }} " | ||
management_gateway: "{{ (index .V1alpha1.Net.Devices 0).Gateway4 }}" | ||
``` | ||
|
||
=== "vAppConfig with supporting template" | ||
=== "vAppConfig with templated properties" | ||
|
||
!!! note "The vApp properties below..." | ||
|
||
The vApp properties used in this example are not standard. All images may define their own properties for configuring the guest's network, or anything else. Please review the image's OVF to understand what properties are available and should be assigned. | ||
|
||
|
||
```yaml | ||
apiVersion: v1 | ||
kind: Secret | ||
metadata: | ||
name: my-secret | ||
namespace: test-ns | ||
name: my-secret | ||
namespace: test-ns | ||
stringData: | ||
# see more details on below Supporting Template Queries section | ||
nameservers: "{{ V1alpha1_FormatNameservers 2 \",\" }}" | ||
management_ip: "{{ V1alpha1_FormatIP \"192.168.1.10\" \"255.255.255.0\" }}" | ||
hostname: "{{ .V1alpha1.VM.Name }} " | ||
management_gateway: "{{ (index .V1alpha1.Net.Devices 0).Gateway4 }}" | ||
# Please see the following section for more information on these functions. | ||
nameservers: "{{ V1alpha1_FormatNameservers 2 \",\" }}" | ||
management_ip: "{{ V1alpha1_FormatIP \"192.168.1.10\" \"255.255.255.0\" }}" | ||
hostname: "{{ .V1alpha1.VM.Name }} " | ||
management_gateway: "{{ (index .V1alpha1.Net.Devices 0).Gateway4 }}" | ||
``` | ||
|
||
:wave: The Golang based template string is a representation of [vm-operator virtualmachinetempl_types v1alpha1 api](https://github.com/vmware-tanzu/vm-operator/blob/25fb865e615d377192a870583ab32973e9fbd32a/api/v1alpha1/virtualmachinetempl_types.go#L4) | ||
|
||
:wave: The fields `nameservers`, `hostname`, `management_ip` and `management_gateway` are derived from image `haproxy-v0.2.0` OVF properties. | ||
```xml | ||
<Property ovf:key="nameservers" ovf:type="string" ovf:userConfigurable="true" ovf:value="1.1.1.1, 1.0.0.1"> | ||
<Label>2.2. DNS</Label> | ||
<Description>A comma-separated list of IP addresses for up to three DNS servers</Description> | ||
</Property> | ||
<Property ovf:key="hostname" ovf:type="string" ovf:userConfigurable="true" ovf:value="ubuntuguest"> | ||
<Description>Specifies the hostname for the appliance</Description> | ||
</Property> | ||
<Property ovf:key="management_ip" ovf:type="string" ovf:userConfigurable="true"> | ||
<Label>2.3. Management IP</Label> | ||
<Description>The static IP address for the appliance on the Management Port Group in CIDR format (.For eg. ip/subnet mask bits). This cannot be DHCP.</Description> | ||
</Property> | ||
``` | ||
|
||
## Supporting Template Queries | ||
To spare users the effort to construct a correct template string, there are some supporting template queries vm-operator provides. | ||
## Templating | ||
|
||
Properties are templated according to the Golang [`text/template`](https://pkg.go.dev/text/template) package. Please refer to Go's documentation for a full understanding of how to construct template queries. | ||
|
||
### Input object | ||
|
||
The object provided to the template engine is the [`VirtualMachineTemplate`](https://github.com/vmware-tanzu/vm-operator/blob/70d22b93b3c454809145725d418c4f6cfebb124c/api/v1alpha1/virtualmachinetempl_types.go#L37-L50) data structure. | ||
|
||
### Pre-defined functions | ||
|
||
The following table lists the functions VM Operator defines and passes into the template engine to make it easier to construct the information required for vApp properties: | ||
|
||
| Query name | Signature | Description | | ||
| -------- | -------- | -------- | | ||
| V1alpha1_FirstIP | `func () string` | Get the first non-loopback IP with CIDR from first NIC. | | ||
| V1alpha1_FirstIPFromNIC | `func (index int) string` | Get non-loopback IP address with CIDR from the ith NIC. if index out of bound, template string won't be parsed. | | ||
| V1alpha1_FormatIP | `func (IP string, netmask string) string` | see below for detailed use case.| | ||
| V1alpha1_FirstNicMacAddr | `func() (string, error)` | Get the first NIC's MAC address. | | ||
| V1alpha1_FormatNameservers| `func (count int, delimiter string) string` | Format the first occurred count of nameservers with specific delimiter (A n.For egative number for count would mean all nameservers). | | ||
| V1alpha1_IP | `func(IP string) string` | Format a static IP address with default netmask CIDR. If IP is not valid, template string won't be parsed. | | ||
| V1alpha1_IPsFromNIC | `func (index int) []string` | List all IPs with CIDR from the ith NIC. if index out of bound, template string won't be parsed. | | ||
| V1alpha1_SubnetMask | `func(cidr string) (string, error)` | Get subnet mask from a CIDR notation IP address and prefix length. | | ||
|
||
### `V1alpha1_FormatIP` | ||
1. Format an IP address with network length. A netmask can be either the length, ex. /24, or the decimal notation, ex. 255.255.255.0. Return IP sans CIDR when input is valid. | ||
|
||
2. Format an IP address with CIDR: | ||
- if input netmask is different with CIDR, replace and return IP with new CIDR. | ||
- if input netmask is empty string, return IP sans CIDR. | ||
- if input netmask is not valid, return empty string. | ||
|
||
**Note** when OVF only takes in IP sans CIDR, use `V1alpha1_FormatIP` and pass empty string as input netmask: | ||
```yaml | ||
management_ip: '{{ V1alpha1_FormatIP V1alpha1_FirstIP "" }}' # return first non-loopback IP from first NIC without CIDR. For eg, "192.168.1.10". | ||
``` | ||
### Examples | ||
```yaml | ||
management_ip: "{{ V1alpha1_FirstIP }}" # return first non-loopback IP with CIDR from first NIC. | ||
management_ip: "{{ V1alpha1_FirstIPFromNIC 1 }}" # return first non-loopback IP with CIDR from second NIC. | ||
management_ip: "{{ V1alpha1_FormatIP \"192.168.1.10\" \"255.255.255.0\" }}" # return IP with CIDR. For eg,"192.168.1.10/24". | ||
management_ip: "{{ V1alpha1_IP \"192.168.1.37\" }}" # return IP with default netmask CIDR. | ||
nameservers: "{{ V1alpha1_FormatNameservers 2 \",\" }}" # return 2 nameservers with "," as delimiter. For eg,"10.20.145.1, 10.20.145.2". | ||
subnet_mask: "{{ V1alpha1_SubnetMask V1alpha1_FirstIP }}" # return the subnet mask of the first non-loopback IP with CIDR from first NIC. For eg, "255.255.255.0". | ||
mac_address: "{{ v1alpha1_FirstNicMacAddr }}" # return the first NIC's MAC Address. | ||
``` | ||
| V1alpha1_FirstIP | `func () string` | Get the first, non-loopback IP address (formatted with network length) from the first NIC. | | ||
| V1alpha1_FirstIPFromNIC | `func (index int) string` | Get the first, non-loopback IP address (formatted with network length) from the n'th NIC. If the specified index is out-of-bounds, the template string is not parsed. | | ||
| V1alpha1_FormatIP | `func (IP string, netmask string) string` | This function may be used to format an IP address with or without a network prefix length. If the provided netmask is empty, then the IP address returned does not include a network length. If the provided netmask is non-empty, then it must be either a length, ex. `/24`, or decimal notation, ex. `255.255.255.0`. | | ||
| V1alpha1_FirstNicMacAddr | `func() (string, error)` | Get the MAC address from the first NIC. | | ||
| V1alpha1_FormatNameservers| `func (count int, delimiter string) string` | Format the first occurred count of nameservers with the provided delimiter. Specify a negative number to include all nameservers. | | ||
| V1alpha1_IP | `func(IP string) string` | Format an IP address with the default netmask CIDR. If the specified IP is invalid, the template string is not parsed. | | ||
| V1alpha1_IPsFromNIC | `func (index int) []string` | List all IPs, formatted with the network length, from the n'th NIC. If the specified index is out-of-bounds, the template string is not parsed. | | ||
| V1alpha1_SubnetMask | `func(cidr string) (string, error)` | Get a subnet mask from an IP address formatted with a network length. | | ||
|