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.
|
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
|
.. attr:: key
|
||||||
:type: str
|
:type: str
|
||||||
|
|
||||||
|
@ -12,10 +12,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import os
|
|
||||||
import math
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
import cachetools.func
|
import cachetools.func
|
||||||
|
|
||||||
@ -41,6 +43,18 @@ def quota_info_from_sku(sku):
|
|||||||
instances=1)
|
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):
|
class AzureInstance(statemachine.Instance):
|
||||||
def __init__(self, vm, nic=None, public_ipv4=None,
|
def __init__(self, vm, nic=None, public_ipv4=None,
|
||||||
public_ipv6=None, sku=None):
|
public_ipv6=None, sku=None):
|
||||||
@ -646,7 +660,7 @@ class AzureAdapter(statemachine.Adapter):
|
|||||||
else:
|
else:
|
||||||
image_reference = {'id': label.cloud_image.image_id}
|
image_reference = {'id': label.cloud_image.image_id}
|
||||||
os_profile = {'computerName': hostname}
|
os_profile = {'computerName': hostname}
|
||||||
if image.username and image.key:
|
if image.key:
|
||||||
linux_config = {
|
linux_config = {
|
||||||
'ssh': {
|
'ssh': {
|
||||||
'publicKeys': [{
|
'publicKeys': [{
|
||||||
@ -657,8 +671,13 @@ class AzureAdapter(statemachine.Adapter):
|
|||||||
},
|
},
|
||||||
"disablePasswordAuthentication": True,
|
"disablePasswordAuthentication": True,
|
||||||
}
|
}
|
||||||
os_profile['adminUsername'] = image.username
|
|
||||||
os_profile['linuxConfiguration'] = linux_config
|
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:
|
if label.custom_data:
|
||||||
os_profile['customData'] = label.custom_data
|
os_profile['customData'] = label.custom_data
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ class AzureProviderCloudImage(ConfigValue):
|
|||||||
}
|
}
|
||||||
self.name = image['name']
|
self.name = image['name']
|
||||||
self.username = image['username']
|
self.username = image['username']
|
||||||
|
self.password = image.get('password')
|
||||||
|
self.generate_password = image.get('generate-password', False)
|
||||||
# TODO(corvus): remove zuul_public_key
|
# TODO(corvus): remove zuul_public_key
|
||||||
self.key = image.get('key', zuul_public_key)
|
self.key = image.get('key', zuul_public_key)
|
||||||
self.image_reference = image.get('image-reference')
|
self.image_reference = image.get('image-reference')
|
||||||
@ -60,6 +62,8 @@ class AzureProviderCloudImage(ConfigValue):
|
|||||||
return v.All({
|
return v.All({
|
||||||
v.Required('name'): str,
|
v.Required('name'): str,
|
||||||
v.Required('username'): str,
|
v.Required('username'): str,
|
||||||
|
'password': str,
|
||||||
|
'generate-password': bool,
|
||||||
# TODO(corvus): make required when zuul_public_key removed
|
# TODO(corvus): make required when zuul_public_key removed
|
||||||
'key': str,
|
'key': str,
|
||||||
v.Exclusive('image-reference', 'spec'): azure_image_reference,
|
v.Exclusive('image-reference', 'spec'): azure_image_reference,
|
||||||
@ -89,6 +93,8 @@ class AzureProviderDiskImage(ConfigValue):
|
|||||||
self.python_path = image.get('python-path')
|
self.python_path = image.get('python-path')
|
||||||
self.shell_type = image.get('shell-type')
|
self.shell_type = image.get('shell-type')
|
||||||
self.username = image.get('username')
|
self.username = image.get('username')
|
||||||
|
self.password = image.get('password')
|
||||||
|
self.generate_password = image.get('generate-password', False)
|
||||||
self.key = image.get('key')
|
self.key = image.get('key')
|
||||||
self.connection_type = image.get('connection-type', 'ssh')
|
self.connection_type = image.get('connection-type', 'ssh')
|
||||||
self.connection_port = image.get(
|
self.connection_port = image.get(
|
||||||
|
@ -122,7 +122,8 @@ class StateMachineNodeLauncher(stats.StatsReporter):
|
|||||||
self.node.connection_type = image.connection_type
|
self.node.connection_type = image.connection_type
|
||||||
self.zk.storeNode(self.node)
|
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
|
retries = self.manager.provider.launch_retries
|
||||||
metadata = {'nodepool_node_id': self.node.id,
|
metadata = {'nodepool_node_id': self.node.id,
|
||||||
'nodepool_pool_name': self.handler.pool.name,
|
'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:
|
labels:
|
||||||
- name: bionic
|
- name: bionic
|
||||||
min-ready: 0
|
min-ready: 0
|
||||||
|
- name: windows-password
|
||||||
|
min-ready: 0
|
||||||
|
- name: windows-generate
|
||||||
|
min-ready: 0
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
- name: azure
|
- name: azure
|
||||||
@ -34,6 +38,22 @@ providers:
|
|||||||
publisher: Canonical
|
publisher: Canonical
|
||||||
version: latest
|
version: latest
|
||||||
offer: UbuntuServer
|
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:
|
pools:
|
||||||
- name: main
|
- name: main
|
||||||
max-servers: 10
|
max-servers: 10
|
||||||
@ -51,3 +71,11 @@ providers:
|
|||||||
systemPurpose: CI
|
systemPurpose: CI
|
||||||
user-data: "This is the user data"
|
user-data: "This is the user data"
|
||||||
custom-data: "This is the custom 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"
|
"/subscriptions/c35cf7df-ed75-4c85-be00-535409a85120"
|
||||||
"/resourceGroups/nodepool/providers/Microsoft.Compute"
|
"/resourceGroups/nodepool/providers/Microsoft.Compute"
|
||||||
"/images/test-image-1234")
|
"/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…
x
Reference in New Issue
Block a user