Add admin password support for Azure driver
Under Azure, an admin password is required in order to launch a VM from a Windows image. Add support for that. Also, shorten the node name to less than 15 characters in order to accomodate Windows restrictions. Change-Id: I899f3e02046ffdb5f9fd19fe90c4bc9afdb01a7c
This commit is contained in:
parent
c6723ea88f
commit
44f3d63973
@ -248,6 +248,24 @@ section of the configuration.
|
||||
|
||||
The username that should be used when connecting to the node.
|
||||
|
||||
.. attr:: password
|
||||
:type: str
|
||||
|
||||
If booting a Windows image, an administrative password is
|
||||
required. Either supply it here, or set
|
||||
:attr:`providers.[azure].cloud-images.generate-password`.
|
||||
Nodepool does not provide the password to requesting clients;
|
||||
to be used it must be provided in some other manner.
|
||||
|
||||
.. attr:: generate-password
|
||||
:type: bool
|
||||
|
||||
If booting a Windows image, an administrative password is
|
||||
required. If the password is not actually used (e.g., the
|
||||
image has key-based authentication enabled), a random
|
||||
password can be provided by enabling this option. The
|
||||
password is not stored anywhere and is not retrievable.
|
||||
|
||||
.. attr:: key
|
||||
:type: str
|
||||
|
||||
|
@ -12,10 +12,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import math
|
||||
import logging
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
|
||||
import cachetools.func
|
||||
|
||||
@ -41,6 +43,18 @@ def quota_info_from_sku(sku):
|
||||
instances=1)
|
||||
|
||||
|
||||
def generate_password():
|
||||
while True:
|
||||
chars = random.choices(string.ascii_lowercase +
|
||||
string.ascii_uppercase +
|
||||
string.digits,
|
||||
k=64)
|
||||
if ((set(string.ascii_lowercase) & set(chars)) and
|
||||
(set(string.ascii_uppercase) & set(chars)) and
|
||||
(set(string.digits) & set(chars))):
|
||||
return(''.join(chars))
|
||||
|
||||
|
||||
class AzureInstance(statemachine.Instance):
|
||||
def __init__(self, vm, nic=None, public_ipv4=None,
|
||||
public_ipv6=None, sku=None):
|
||||
@ -646,7 +660,7 @@ class AzureAdapter(statemachine.Adapter):
|
||||
else:
|
||||
image_reference = {'id': label.cloud_image.image_id}
|
||||
os_profile = {'computerName': hostname}
|
||||
if image.username and image.key:
|
||||
if image.key:
|
||||
linux_config = {
|
||||
'ssh': {
|
||||
'publicKeys': [{
|
||||
@ -657,8 +671,13 @@ class AzureAdapter(statemachine.Adapter):
|
||||
},
|
||||
"disablePasswordAuthentication": True,
|
||||
}
|
||||
os_profile['adminUsername'] = image.username
|
||||
os_profile['linuxConfiguration'] = linux_config
|
||||
if image.username:
|
||||
os_profile['adminUsername'] = image.username
|
||||
if image.password:
|
||||
os_profile['adminPassword'] = image.password
|
||||
elif image.generate_password:
|
||||
os_profile['adminPassword'] = generate_password()
|
||||
if label.custom_data:
|
||||
os_profile['customData'] = label.custom_data
|
||||
|
||||
|
@ -32,6 +32,8 @@ class AzureProviderCloudImage(ConfigValue):
|
||||
}
|
||||
self.name = image['name']
|
||||
self.username = image['username']
|
||||
self.password = image.get('password')
|
||||
self.generate_password = image.get('generate-password', False)
|
||||
# TODO(corvus): remove zuul_public_key
|
||||
self.key = image.get('key', zuul_public_key)
|
||||
self.image_reference = image.get('image-reference')
|
||||
@ -60,6 +62,8 @@ class AzureProviderCloudImage(ConfigValue):
|
||||
return v.All({
|
||||
v.Required('name'): str,
|
||||
v.Required('username'): str,
|
||||
'password': str,
|
||||
'generate-password': bool,
|
||||
# TODO(corvus): make required when zuul_public_key removed
|
||||
'key': str,
|
||||
v.Exclusive('image-reference', 'spec'): azure_image_reference,
|
||||
@ -89,6 +93,8 @@ class AzureProviderDiskImage(ConfigValue):
|
||||
self.python_path = image.get('python-path')
|
||||
self.shell_type = image.get('shell-type')
|
||||
self.username = image.get('username')
|
||||
self.password = image.get('password')
|
||||
self.generate_password = image.get('generate-password', False)
|
||||
self.key = image.get('key')
|
||||
self.connection_type = image.get('connection-type', 'ssh')
|
||||
self.connection_port = image.get(
|
||||
|
@ -122,7 +122,8 @@ class StateMachineNodeLauncher(stats.StatsReporter):
|
||||
self.node.connection_type = image.connection_type
|
||||
self.zk.storeNode(self.node)
|
||||
|
||||
hostname = 'nodepool-' + self.node.id
|
||||
# Windows computer names can be no more than 15 chars long.
|
||||
hostname = 'np' + self.node.id
|
||||
retries = self.manager.provider.launch_retries
|
||||
metadata = {'nodepool_node_id': self.node.id,
|
||||
'nodepool_pool_name': self.handler.pool.name,
|
||||
|
28
nodepool/tests/fixtures/azure.yaml
vendored
28
nodepool/tests/fixtures/azure.yaml
vendored
@ -15,6 +15,10 @@ zookeeper-tls:
|
||||
labels:
|
||||
- name: bionic
|
||||
min-ready: 0
|
||||
- name: windows-password
|
||||
min-ready: 0
|
||||
- name: windows-generate
|
||||
min-ready: 0
|
||||
|
||||
providers:
|
||||
- name: azure
|
||||
@ -34,6 +38,22 @@ providers:
|
||||
publisher: Canonical
|
||||
version: latest
|
||||
offer: UbuntuServer
|
||||
- name: windows-password
|
||||
image-reference:
|
||||
sku: 2022-datacenter-azure-edition
|
||||
publisher: MicrosoftWindowsServer
|
||||
version: latest
|
||||
offer: WindowsServer
|
||||
username: foobar
|
||||
password: reallybadpassword123
|
||||
- name: windows-generate
|
||||
image-reference:
|
||||
sku: 2022-datacenter-azure-edition
|
||||
publisher: MicrosoftWindowsServer
|
||||
version: latest
|
||||
offer: WindowsServer
|
||||
username: foobar
|
||||
generate-password: True
|
||||
pools:
|
||||
- name: main
|
||||
max-servers: 10
|
||||
@ -51,3 +71,11 @@ providers:
|
||||
systemPurpose: CI
|
||||
user-data: "This is the user data"
|
||||
custom-data: "This is the custom data"
|
||||
- name: windows-password
|
||||
cloud-image: windows-password
|
||||
hardware-profile:
|
||||
vm-size: Standard_B1ls
|
||||
- name: windows-generate
|
||||
cloud-image: windows-generate
|
||||
hardware-profile:
|
||||
vm-size: Standard_B1ls
|
||||
|
@ -139,3 +139,67 @@ class TestDriverAzure(tests.DBTestCase):
|
||||
"/subscriptions/c35cf7df-ed75-4c85-be00-535409a85120"
|
||||
"/resourceGroups/nodepool/providers/Microsoft.Compute"
|
||||
"/images/test-image-1234")
|
||||
|
||||
def test_azure_windows_image_password(self):
|
||||
configfile = self.setup_config(
|
||||
'azure.yaml',
|
||||
auth_path=self.fake_azure.auth_file.name)
|
||||
pool = self.useNodepool(configfile, watermark_sleep=1)
|
||||
pool.start()
|
||||
req = zk.NodeRequest()
|
||||
req.state = zk.REQUESTED
|
||||
req.node_types.append('windows-password')
|
||||
|
||||
self.zk.storeNodeRequest(req)
|
||||
req = self.waitForNodeRequest(req)
|
||||
|
||||
self.assertEqual(req.state, zk.FULFILLED)
|
||||
self.assertNotEqual(req.nodes, [])
|
||||
node = self.zk.getNode(req.nodes[0])
|
||||
self.assertEqual(node.allocated_to, req.id)
|
||||
self.assertEqual(node.state, zk.READY)
|
||||
self.assertIsNotNone(node.launcher)
|
||||
self.assertEqual(node.connection_type, 'ssh')
|
||||
self.assertEqual(node.attributes,
|
||||
{'key1': 'value1', 'key2': 'value2'})
|
||||
self.assertEqual(node.host_keys, ['ssh-rsa FAKEKEY'])
|
||||
self.assertEqual(
|
||||
self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['properties']['osProfile']['adminUsername'],
|
||||
'foobar')
|
||||
self.assertEqual(
|
||||
self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['properties']['osProfile']['adminPassword'],
|
||||
'reallybadpassword123')
|
||||
|
||||
def test_azure_windows_image_generate(self):
|
||||
configfile = self.setup_config(
|
||||
'azure.yaml',
|
||||
auth_path=self.fake_azure.auth_file.name)
|
||||
pool = self.useNodepool(configfile, watermark_sleep=1)
|
||||
pool.start()
|
||||
req = zk.NodeRequest()
|
||||
req.state = zk.REQUESTED
|
||||
req.node_types.append('windows-generate')
|
||||
|
||||
self.zk.storeNodeRequest(req)
|
||||
req = self.waitForNodeRequest(req)
|
||||
|
||||
self.assertEqual(req.state, zk.FULFILLED)
|
||||
self.assertNotEqual(req.nodes, [])
|
||||
node = self.zk.getNode(req.nodes[0])
|
||||
self.assertEqual(node.allocated_to, req.id)
|
||||
self.assertEqual(node.state, zk.READY)
|
||||
self.assertIsNotNone(node.launcher)
|
||||
self.assertEqual(node.connection_type, 'ssh')
|
||||
self.assertEqual(node.attributes,
|
||||
{'key1': 'value1', 'key2': 'value2'})
|
||||
self.assertEqual(node.host_keys, ['ssh-rsa FAKEKEY'])
|
||||
self.assertEqual(
|
||||
self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['properties']['osProfile']['adminUsername'],
|
||||
'foobar')
|
||||
self.assertEqual(
|
||||
len(self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['properties']['osProfile']['adminPassword']),
|
||||
64)
|
||||
|
5
releasenotes/notes/azure-password-c70896bf49deab8a.yaml
Normal file
5
releasenotes/notes/azure-password-c70896bf49deab8a.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The Azure driver now supports setting an admin password, which is
|
||||
required in order to launch Windows images on Azure.
|
Loading…
Reference in New Issue
Block a user