Co-authored-by: Mangirdas Judeikis <Mangirdas@Judeikis.LT>
This commit is contained in:
Jim Minter 2019-12-13 12:09:53 -06:00
Родитель 972eb709f3
Коммит a3096fea90
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 0730CBDA10D1A2D3
23 изменённых файлов: 622 добавлений и 166 удалений

1
.env Normal file
Просмотреть файл

@ -0,0 +1 @@
PYTHONPATH=python/az/aro

Просмотреть файл

@ -1,4 +1,4 @@
name: pull_request-test
name: pull_request-test-go
on:
pull_request:
types:
@ -21,5 +21,5 @@ jobs:
- name: Test
run: |
set -x
make test
make test-go
[[ -z "$(git status -s)" ]]

27
.github/workflows/pull_request-test-python.yaml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,27 @@
name: pull_request-test-python
on:
pull_request:
types:
- opened
- synchronize
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- 2.7
- 3.5.7
- 3.6.9
steps:
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Check out source
uses: actions/checkout@v1
- name: Test
run: |
set -x
pip install virtualenv
make test-python

9
.gitignore поставляемый
Просмотреть файл

@ -1,9 +1,16 @@
__pycache__
*.egg-info
*.pyc
/.vscode
/*.crt
/*.key
/*.pem
/*.kubeconfig
/*.pem
/env*
!/env.example
/id_rsa
/pyenv*
/python/az/aro/build
/python/az/aro/dist
/rp
/secrets

Просмотреть файл

@ -3,8 +3,13 @@ COMMIT = $(shell git rev-parse --short HEAD)$(shell [[ $$(git status --porcelain
rp: generate
go build -ldflags "-X main.gitCommit=$(COMMIT)" ./cmd/rp
az:
cd python/az/aro && python ./setup.py bdist_egg
clean:
rm -f rp
rm -rf python/az/aro/{aro.egg-info,build,dist} rp
find python -type f -name '*.pyc' -delete
find python -type d -name __pycache__ -delete
client: generate
rm -rf pkg/client python/client
@ -33,6 +38,7 @@ client: generate
sudo chown -R $(USER):$(USER) pkg/client python/client
rm -rf python/client/azure/mgmt/redhatopenshift/v2019_12_31_preview/aio
>python/client/__init__.py
go run ./vendor/golang.org/x/tools/cmd/goimports -w -local=github.com/jim-minter/rp pkg/client
@ -50,7 +56,7 @@ secrets:
secrets-update:
oc create secret generic aro-v4-dev --from-file=secrets --dry-run -o yaml | oc apply -f -
test: generate
test-go: generate
go build ./...
gofmt -s -w cmd hack pkg
@ -63,4 +69,14 @@ test: generate
go vet ./...
go test ./...
.PHONY: rp clean client generate image secrets secrets-update test
test-python:
virtualenv --python=/usr/bin/python${PYTHON_VERSION} pyenv${PYTHON_VERSION}
. pyenv${PYTHON_VERSION}/bin/activate && \
pip install azdev && \
azdev setup -r . && \
sed -i -e "s|^dev_sources = $(PWD)$$|dev_sources = $(PWD)/python|" ~/.azure/config && \
$(MAKE) az && \
azdev linter && \
azdev style
.PHONY: rp az clean client generate image secrets secrets-update test-go test-python

Просмотреть файл

@ -13,6 +13,17 @@
az login
```
1. Add the ARO preview extension to `az`:
```
make az
cat >>~/.azure/config <<EOF
[extension]
dev_sources = $(go env GOPATH)/src/github.com/jim-minter/rp/python
EOF
```
1. You will need a publicly resolvable **DNS zone** resource in your Azure
subscription. *RH ARO engineering*: use the `osadev.cloud` zone in the `dns`
resource group.
@ -62,15 +73,6 @@
*RH ARO engineering*: use the `localhost` key and certificate in the shared
`secrets/localhost.pem` file.
1. You will need your own **cluster AAD application** with client secret
authentication enabled.
```
AZURE_CLUSTER_CLIENT_ID="$(az ad app create --display-name "user-$USER-v4" --query appId -o tsv)"
az ad sp create --id "$AZURE_CLUSTER_CLIENT_ID"
AZURE_CLUSTER_CLIENT_SECRET="$(az ad app credential reset --id "$AZURE_CLUSTER_CLIENT_ID" --query password -o tsv)"
```
1. Copy env.example to env, edit the values and source the env file. This file
holds (only) the environment variables necessary for the RP to run.
@ -85,8 +87,6 @@
* AZURE_FP_CLIENT_ID: RP "first party" application client UUID
* AZURE_CLIENT_ID: RP AAD application client UUID
* AZURE_CLIENT_SECRET: RP AAD application client secret
* AZURE_CLUSTER_CLIENT_ID: Cluster AAD application client UUID
* AZURE_CLUSTER_CLIENT_SECRET: Cluster AAD application client secret
* PULL_SECRET: A cluster pull secret retrieved from [Red Hat OpenShift Cluster Manager](https://cloud.redhat.com/openshift/install/azure/installer-provisioned)
@ -156,13 +156,11 @@ go run ./cmd/rp
## Useful commands
```
export VNET_RESOURCEGROUP="$RESOURCEGROUP-vnet"
VNET_RESOURCEGROUP="$RESOURCEGROUP-vnet"
az group create -g "$VNET_RESOURCEGROUP" -l "$LOCATION"
az network vnet create -g "$VNET_RESOURCEGROUP" -n vnet --address-prefixes 10.0.0.0/9
az role assignment create --role "ARO v4 Development Subnet Contributor" --assignee-object-id "$(az ad sp list --all --query "[?appId=='$AZURE_FP_CLIENT_ID'].objectId" -o tsv)" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$VNET_RESOURCEGROUP/providers/Microsoft.Network/virtualNetworks/vnet"
az role assignment create --role "ARO v4 Development Subnet Contributor" --assignee-object-id "$(az ad sp list --all --query "[?appId=='$AZURE_CLUSTER_CLIENT_ID'].objectId" -o tsv)" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$VNET_RESOURCEGROUP/providers/Microsoft.Network/virtualNetworks/vnet"
export CLUSTER=cluster
CLUSTER=cluster
```
* Register a subscription:
@ -177,31 +175,31 @@ curl -k -X PUT "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID?api-
az network vnet subnet create -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-master" --address-prefixes "10.$((RANDOM & 127)).$((RANDOM & 255)).0/24"
az network vnet subnet create -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-worker" --address-prefixes "10.$((RANDOM & 127)).$((RANDOM & 255)).0/24"
envsubst <examples/cluster-v20191231.json | curl -k -X PUT "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters/$CLUSTER?api-version=2019-12-31-preview" -H 'Content-Type: application/json' -d @-
az aro create -g "$RESOURCEGROUP" -n "$CLUSTER" --vnet-resource-group "$VNET_RESOURCEGROUP" --vnet vnet --master-subnet "$CLUSTER-master" --worker-subnet "$CLUSTER-worker" --location="$LOCATION"
```
* Get a cluster:
```
curl -k "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters/$CLUSTER?api-version=2019-12-31-preview"
az aro show -g "$RESOURCEGROUP" -n "$CLUSTER"
```
* Get a cluster's kubeadmin credentials:
```
curl -k -X POST "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters/$CLUSTER/credentials?api-version=2019-12-31-preview" -H 'Content-Type: application/json' -d '{}'
az aro get-credentials -g "$RESOURCEGROUP" -n "$CLUSTER"
```
* List clusters in resource group:
```
curl -k "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters?api-version=2019-12-31-preview"
az aro list -g "$RESOURCEGROUP"
```
* List clusters in subscription:
```
curl -k "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/providers/Microsoft.RedHatOpenShift/openShiftClusters?api-version=2019-12-31-preview"
az aro list
```
* Scale a cluster:
@ -209,13 +207,13 @@ curl -k "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/providers/M
```
COUNT=4
curl -k -X PATCH "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters/$CLUSTER?api-version=2019-12-31-preview" -H 'Content-Type: application/json' -d '{"properties": {"workerProfiles": [{"name": "worker", "count": '"$COUNT"'}]}}'
az aro update -g "$RESOURCEGROUP" -n "$CLUSTER" --worker-count "$COUNT"
```
* Delete a cluster:
```
curl -k -X DELETE "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters/$CLUSTER?api-version=2019-12-31-preview"
az aro delete -g "$RESOURCEGROUP" -n "$CLUSTER"
az network vnet subnet delete -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-master"
az network vnet subnet delete -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-worker"

Просмотреть файл

@ -1,14 +1,11 @@
# Deploy production cluster
export VNET_RESOURCEGROUP=$RESOURCEGROUP-vnet
export CLUSTER=cluster
VNET_RESOURCEGROUP=$RESOURCEGROUP-vnet
CLUSTER=cluster
az group create -g "$VNET_RESOURCEGROUP" -l "$LOCATION"
az network vnet create -g "$VNET_RESOURCEGROUP" -n vnet --address-prefixes 10.0.0.0/9
az network vnet subnet create -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-master" --address-prefixes 10.$((RANDOM & 127)).$((RANDOM & 255)).0/24
az network vnet subnet create -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-worker" --address-prefixes 10.$((RANDOM & 127)).$((RANDOM & 255)).0/24
az role assignment create --role "ARO v4 Development Subnet Contributor" --assignee-object-id "$(az ad sp list --all --query "[?appId=='f1dd0a37-89c6-4e07-bcd1-ffd3d43d8875'].objectId" -o tsv)" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourceGroups/$VNET_RESOURCEGROUP/providers/Microsoft.Network/virtualNetworks/vnet"
az aro create --resource-group $RESOURCEGROUP --name $CLUSTER --client-id $AZURE_CLUSTER_CLIENT_ID --client-secret $AZURE_CLUSTER_CLIENT_SECRET --vnet-rg-name $VNET_RESOURCEGROUP
az aro create -g "$RESOURCEGROUP" -n "$CLUSTER" --vnet-resource-group "$VNET_RESOURCEGROUP" --vnet vnet --master-subnet "$CLUSTER-master" --worker-subnet "$CLUSTER-worker"

Просмотреть файл

@ -4,8 +4,6 @@ export RP_MODE=development
# RH ARO engineering: uncomment the following stanza only and run `make secrets`
#. secrets/env
#export AZURE_CLUSTER_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
#export AZURE_CLUSTER_CLIENT_SECRET=<secret>
# non-RH ARO engineering: uncomment from here
#export AZURE_TENANT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
@ -15,6 +13,4 @@ export RP_MODE=development
#export AZURE_FP_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
#export AZURE_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
#export AZURE_CLIENT_SECRET=<secret>
#export AZURE_CLUSTER_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
#export AZURE_CLUSTER_CLIENT_SECRET=<secret>
#export PULL_SECRET='<secret-json-object>'

Просмотреть файл

@ -1,29 +0,0 @@
{
"id": "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$RESOURCEGROUP/providers/Microsoft.RedHatOpenShift/openShiftClusters/$CLUSTER",
"name": "$CLUSTER",
"type": "Microsoft.RedHatOpenShift/openShiftClusters",
"location": "$LOCATION",
"properties": {
"servicePrincipalProfile": {
"clientId": "$AZURE_CLUSTER_CLIENT_ID",
"clientSecret": "$AZURE_CLUSTER_CLIENT_SECRET"
},
"networkProfile": {
"podCidr": "10.128.0.0/14",
"serviceCidr": "172.30.0.0/16"
},
"masterProfile": {
"vmSize": "Standard_D8s_v3",
"subnetId": "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$VNET_RESOURCEGROUP/providers/Microsoft.Network/virtualNetworks/vnet/subnets/$CLUSTER-master"
},
"workerProfiles": [
{
"name": "worker",
"vmSize": "Standard_D2s_v3",
"diskSizeGB": 128,
"subnetId": "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$VNET_RESOURCEGROUP/providers/Microsoft.Network/virtualNetworks/vnet/subnets/$CLUSTER-worker",
"count": 3
}
]
}
}

Просмотреть файл

@ -1,26 +1,22 @@
from azext_aro._client_factory import cf_aro
from azext_aro._params import load_arguments
from azext_aro.commands import load_command_table
from azure.cli.core import AzCommandsLoader
from azext_aro._help import helps # pylint: disable=unused-import
from azure.cli.core.commands import CliCommandType
class AroCommandsLoader(AzCommandsLoader):
def __init__(self, cli_ctx=None):
from azure.cli.core.commands import CliCommandType
from azext_aro._client_factory import cf_aro
aro_custom = CliCommandType(
operations_tmpl='azext_aro.custom#{}',
client_factory=cf_aro)
aro_custom = CliCommandType(operations_tmpl='azext_aro.custom#{}',
client_factory=cf_aro)
super(AroCommandsLoader, self).__init__(cli_ctx=cli_ctx,
custom_command_type=aro_custom)
custom_command_type=aro_custom)
def load_command_table(self, args):
from azext_aro.commands import load_command_table
load_command_table(self, args)
return self.command_table
def load_arguments(self, command):
from azext_aro._params import load_arguments
load_arguments(self, command)

Просмотреть файл

@ -0,0 +1,68 @@
import datetime
import uuid
from azure.cli.core._profile import Profile
from azure.cli.core.commands.client_factory import configure_common_settings
from azure.graphrbac import GraphRbacManagementClient
from azure.graphrbac.models import ApplicationCreateParameters
from azure.graphrbac.models import PasswordCredential
from azure.graphrbac.models import ServicePrincipalCreateParameters
class AADManager(object):
MANAGED_APP_PREFIX = "https://az.aro.azure.com/"
def __init__(self, cli_ctx):
profile = Profile(cli_ctx=cli_ctx)
credentials, _, tenant_id = profile.get_login_credentials(
resource=cli_ctx.cloud.endpoints.active_directory_graph_resource_id)
self.client = GraphRbacManagementClient(
credentials, tenant_id, base_url=cli_ctx.cloud.endpoints.active_directory_graph_resource_id)
configure_common_settings(cli_ctx, self.client)
def createManagedApplication(self, display_name):
password = uuid.uuid4()
try:
end_date = datetime.datetime(2299, 12, 31, tzinfo=datetime.timezone.utc)
except AttributeError:
end_date = datetime.datetime(2299, 12, 31)
app = self.client.applications.create(ApplicationCreateParameters(
display_name=display_name,
identifier_uris=[
self.MANAGED_APP_PREFIX + str(uuid.uuid4()),
],
password_credentials=[
PasswordCredential(
end_date=end_date,
value=password,
),
],
))
return app, password
def getApplication(self, app_id):
apps = list(self.client.applications.list(
filter="appId eq '%s'" % app_id))
if apps:
return apps[0]
return None
def deleteManagedApplication(self, app_id):
app = self.getApplication(app_id)
if app and app.identifier_uris and app.identifier_uris[0].startswith(self.MANAGED_APP_PREFIX):
self.client.applications.delete(app.object_id)
def getServicePrincipal(self, app_id):
sps = list(self.client.service_principals.list(
filter="appId eq '%s'" % app_id))
if sps:
return sps[0]
return None
def createServicePrincipal(self, app_id):
return self.client.service_principals.create(ServicePrincipalCreateParameters(
app_id=app_id,
))

Просмотреть файл

@ -1,7 +1,17 @@
def cf_aro(cli_ctx, *_):
import urllib3
from azure.cli.core.commands.client_factory import get_mgmt_service_client
# TODO: Replace CONTOSO with the appropriate label and uncomment
# from azure.mgmt.CONTOSO import CONTOSOManagementClient
# return get_mgmt_service_client(cli_ctx, CONTOSOManagementClient)
return None
from azext_aro.custom import rp_mode_development
from azext_aro.vendored_sdks.azure.mgmt.redhatopenshift.v2019_12_31_preview import AzureRedHatOpenShiftClient
from azure.cli.core.commands.client_factory import get_mgmt_service_client
def cf_aro(cli_ctx, *_):
client = get_mgmt_service_client(
cli_ctx, AzureRedHatOpenShiftClient).open_shift_clusters
if rp_mode_development():
client.config.base_url = "https://localhost:8443/"
client.config.connection.verify = False
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
return client

Просмотреть файл

@ -0,0 +1,20 @@
import collections
from msrestazure.tools import parse_resource_id
def aro_list_table_format(results):
return [aro_show_table_format(r) for r in results]
def aro_show_table_format(result):
parts = parse_resource_id(result["id"])
return collections.OrderedDict(
Name=result["name"],
ResourceGroup=parts["resource_group"],
Location=result["location"],
ProvisioningState=result["provisioningState"],
WorkerCount=result["workerProfiles"][0]["count"],
ConsoleURL=result["consoleUrl"],
)

Просмотреть файл

@ -1,34 +1,37 @@
# coding=utf-8
from knack.help_files import helps # pylint: disable=unused-import
from knack.help_files import helps
helps['aro'] = """
type: group
short-summary: Commands to manage Aros.
short-summary: Manage Azure Red Hat OpenShift clusters.
"""
helps['aro create'] = """
type: command
short-summary: Create a Aro.
short-summary: Create a cluster.
"""
helps['aro list'] = """
type: command
short-summary: List Aros.
short-summary: List clusters.
"""
# helps['aro delete'] = """
# type: command
# short-summary: Delete a Aro.
# """
helps['aro delete'] = """
type: command
short-summary: Delete a cluster.
"""
# helps['aro show'] = """
# type: command
# short-summary: Show details of a Aro.
# """
helps['aro show'] = """
type: command
short-summary: Get the details of a cluster.
"""
# helps['aro update'] = """
# type: command
# short-summary: Update a Aro.
# """
helps['aro update'] = """
type: command
short-summary: Update a cluster.
"""
helps['aro get-credentials'] = """
type: command
short-summary: Get credentials of a cluster.
"""

Просмотреть файл

@ -1,19 +1,62 @@
# pylint: disable=line-too-long
from knack.arguments import CLIArgumentType
from azext_aro._validators import validate_cidr
from azext_aro._validators import validate_client_id
from azext_aro._validators import validate_client_secret
from azext_aro._validators import validate_subnet
from azext_aro._validators import validate_vnet
from azext_aro._validators import validate_worker_count
from azext_aro._validators import validate_worker_vm_disk_size_gb
from azure.cli.core.commands.parameters import name_type
from azure.cli.core.commands.parameters import resource_group_name_type
from azure.cli.core.commands.parameters import tags_type
from azure.cli.core.commands.validators import get_default_location_from_resource_group
def load_arguments(self, _):
from azure.cli.core.commands.parameters import tags_type
from azure.cli.core.commands.validators import get_default_location_from_resource_group
aro_name_type = CLIArgumentType(options_list='--aro-name-name', help='Name of the Aro.', id_part='name')
with self.argument_context('aro') as c:
c.argument('tags', tags_type)
c.argument('location', validator=get_default_location_from_resource_group)
c.argument('aro_name', aro_name_type, options_list=['--name', '-n'])
c.argument('location',
validator=get_default_location_from_resource_group)
c.argument('resource_name',
name_type,
help='Name of cluster.')
c.argument('tags',
tags_type)
with self.argument_context('aro list') as c:
c.argument('aro_name', aro_name_type, id_part=None)
c.argument('client_id',
help='Client ID of cluster service principal.',
validator=validate_client_id)
c.argument('client_secret',
help='Client secret of cluster service principal.',
validator=validate_client_secret)
c.argument('pod_cidr',
help='CIDR of pod network.',
validator=validate_cidr('pod_cidr'))
c.argument('service_cidr',
help='CIDR of service network.',
validator=validate_cidr('service_cidr'))
c.argument('master_vm_size',
help='Size of master VMs.')
c.argument('worker_vm_size',
help='Size of worker VMs.')
c.argument('worker_vm_disk_size_gb',
help='Disk size in GB of worker VMs.',
validator=validate_worker_vm_disk_size_gb)
c.argument('worker_count',
help='Count of worker VMs.',
validator=validate_worker_count)
c.argument('vnet_resource_group_name',
resource_group_name_type,
options_list=['--vnet-resource-group'],
help='Name of vnet resource group.')
c.argument('vnet',
help='Name or ID of vnet. If name is supplied, `--vnet-resource-group` must be supplied.',
validator=validate_vnet)
c.argument('master_subnet',
help='Name or ID of master vnet subnet. If name is supplied, `--vnet` must be supplied.',
validator=validate_subnet('master_subnet'))
c.argument('worker_subnet',
help='Name or ID of worker vnet subnet. If name is supplied, `--vnet` must be supplied.',
validator=validate_subnet('worker_subnet'))

Просмотреть файл

@ -0,0 +1,35 @@
import uuid
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.profiles import get_sdk
from azure.cli.core.profiles import ResourceType
from msrestazure.azure_exceptions import CloudError
from msrestazure.tools import resource_id
CONTRIBUTOR = "b24988ac-6180-42a0-ab88-20f7382dd24c"
def assign_contributor_to_vnet(cli_ctx, vnet, object_id):
client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION)
RoleAssignmentCreateParameters = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION,
'RoleAssignmentCreateParameters', mod='models',
operation_group='role_assignments')
try:
client.role_assignments.create(vnet, uuid.uuid4(), RoleAssignmentCreateParameters(
role_definition_id=resource_id(
subscription=get_subscription_id(cli_ctx),
namespace='Microsoft.Authorization',
type='roleDefinitions',
name=CONTRIBUTOR,
),
principal_id=object_id,
principal_type="ServicePrincipal",
))
except CloudError as err:
if err.status_code == 409:
return
raise err

Просмотреть файл

@ -1,14 +1,162 @@
def example_name_or_id_validator(cmd, namespace):
# Example of a storage account name or ID validator.
# See: https://github.com/Azure/azure-cli/blob/dev/doc/authoring_command_modules/authoring_commands.md#supporting-name-or-id-parameters
from azure.cli.core.commands.client_factory import get_subscription_id
from msrestazure.tools import is_valid_resource_id, resource_id
if namespace.storage_account:
if not is_valid_resource_id(namespace.RESOURCE):
namespace.storage_account = resource_id(
import ipaddress
import uuid
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.profiles import ResourceType
from knack.util import CLIError
from msrestazure.azure_exceptions import CloudError
from msrestazure.tools import is_valid_resource_id
from msrestazure.tools import parse_resource_id
from msrestazure.tools import resource_id
def validate_cidr(key):
def _validate_cidr(namespace):
cidr = getattr(namespace, key)
if cidr is not None:
try:
ipaddress.IPv4Network(cidr)
except ValueError:
raise CLIError("Invalid --%s '%s'." %
(key.replace('_', '-'), cidr))
return _validate_cidr
def validate_client_id(namespace):
if namespace.client_id is not None:
try:
uuid.UUID(namespace.client_id)
except ValueError:
raise CLIError("Invalid --client-id '%s'." % namespace.client_id)
if namespace.client_secret is None or not str(namespace.client_secret):
raise CLIError("Must specify --client-secret with --client-id.")
def validate_client_secret(namespace):
if namespace.client_secret is not None:
if namespace.client_id is not None or not str(namespace.client_id):
raise CLIError("Must specify --client-id with --client-secret.")
def _validate_int(key, i):
try:
i = int(i)
except ValueError:
raise CLIError("Invalid --%s '%s'." % (key.replace('_', '-'), i))
return i
def validate_subnet(key):
def _validate_subnet(cmd, namespace):
subnet = getattr(namespace, key)
if not is_valid_resource_id(subnet):
if not namespace.vnet:
raise CLIError(
"Must specify --vnet if --%s is not an id." % key.replace('_', '-'))
validate_vnet(cmd, namespace)
subnet = namespace.vnet + "/subnets/" + subnet
setattr(namespace, key, subnet)
parts = parse_resource_id(subnet)
if parts["subscription"] != get_subscription_id(cmd.cli_ctx):
raise CLIError("--%s subscription '%s' must equal cluster subscription." %
(key.replace('_', '-'), parts["subscription"]))
if parts["namespace"].lower() != "microsoft.network":
raise CLIError("--%s namespace '%s' must equal Microsoft.Network." %
(key.replace('_', '-'), parts["namespace"]))
if parts["type"].lower() != "virtualnetworks":
raise CLIError("--%s type '%s' must equal virtualNetworks." %
(key.replace('_', '-'), parts["type"]))
if parts["last_child_num"] != 1:
raise CLIError("--%s '%s' must have one child." %
(key.replace('_', '-'), subnet))
if "child_namespace_1" in parts:
raise CLIError("--%s '%s' must not have child namespace." %
(key.replace('_', '-'), subnet))
if parts["child_type_1"].lower() != "subnets":
raise CLIError("--%s child type '%s' must equal subnets." %
(key.replace('_', '-'), subnet))
client = get_mgmt_service_client(
cmd.cli_ctx, ResourceType.MGMT_NETWORK)
try:
client.subnets.get(parts["resource_group"],
parts["name"], parts["child_name_1"])
except CloudError as err:
raise CLIError(err.message)
return _validate_subnet
def validate_subnets(master_subnet, worker_subnet):
master_parts = parse_resource_id(master_subnet)
worker_parts = parse_resource_id(worker_subnet)
if master_parts["resource_group"].lower() != worker_parts["resource_group"].lower():
raise CLIError("--master-subnet resource group '%s' must equal --worker-subnet resource group '%s'." %
(master_parts["resource_group"], worker_parts["resource_group"]))
if master_parts["name"].lower() != worker_parts["name"].lower():
raise CLIError("--master-subnet vnet name '%s' must equal --worker-subnet vnet name '%s'." %
(master_parts["name"], worker_parts["name"]))
if master_parts["child_name_1"].lower() == worker_parts["child_name_1"].lower():
raise CLIError("--master-subnet name '%s' must not equal --worker-subnet name '%s'." %
(master_parts["child_name_1"], worker_parts["child_name_1"]))
return resource_id(
subscription=master_parts["subscription"],
resource_group=master_parts["resource_group"],
namespace='Microsoft.Network',
type='virtualNetworks',
name=master_parts["name"],
)
def validate_vnet(cmd, namespace):
if namespace.vnet:
if not is_valid_resource_id(namespace.vnet):
if not namespace.vnet_resource_group_name:
raise CLIError(
"Must specify --vnet-resource-group-name if --vnet is not an id.")
namespace.vnet = resource_id(
subscription=get_subscription_id(cmd.cli_ctx),
resource_group=namespace.resource_group_name,
namespace='Microsoft.Storage',
type='storageAccounts',
name=namespace.storage_account
resource_group=namespace.vnet_resource_group_name,
namespace='Microsoft.Network',
type='virtualNetworks',
name=namespace.vnet,
)
def validate_worker_count(namespace):
if namespace.worker_count:
namespace.worker_count = _validate_int(
"worker_count", namespace.worker_count)
if namespace.worker_count < 3:
raise CLIError(
"--worker-count must be greater than or equal to 3.")
def validate_worker_vm_disk_size_gb(namespace):
if namespace.worker_vm_disk_size_gb:
namespace.worker_vm_disk_size_gb = _validate_int(
"worker_vm_disk_size_gb", namespace.worker_vm_disk_size_gb)
if namespace.worker_vm_disk_size_gb < 128:
raise CLIError(
"--worker_vm_disk_size_gb must be greater than or equal to 128.")

Просмотреть файл

@ -1,23 +1,20 @@
# pylint: disable=line-too-long
from azure.cli.core.commands import CliCommandType
from azext_aro._client_factory import cf_aro
from azext_aro._format import aro_show_table_format
from azext_aro._format import aro_list_table_format
from azext_aro._help import helps # pylint: disable=unused-import
def load_command_table(self, _):
aro_sdk = CliCommandType(
operations_tmpl='azext_aro.vendored_sdks.azure.mgmt.redhatopenshift.v2019_12_31_preview.operations#OpenShiftClustersOperations.{}', # pylint: disable=line-too-long
client_factory=cf_aro)
# TODO: Add command type here
# aro_sdk = CliCommandType(
# operations_tmpl='<PATH>.operations#None.{}',
# client_factory=cf_aro)
with self.command_group('aro', aro_sdk, client_factory=cf_aro, is_preview=True) as g:
g.custom_command('create', 'aro_create', supports_no_wait=True)
g.custom_command('delete', 'aro_delete', supports_no_wait=True)
g.custom_command('list', 'aro_list', table_transformer=aro_list_table_format)
g.custom_show_command('show', 'aro_show', table_transformer=aro_show_table_format)
g.custom_command('update', 'aro_update', supports_no_wait=True)
with self.command_group('aro') as g:
g.custom_command('create', 'create_aro')
# g.command('delete', 'delete')
g.custom_command('list', 'list_aro')
# g.show_command('show', 'get')
# g.generic_update_command('update', setter_name='update', custom_func_name='update_aro')
with self.command_group('aro', is_preview=True):
pass
g.custom_command('get-credentials', 'aro_get_credentials')

Просмотреть файл

@ -1,15 +1,139 @@
from knack.util import CLIError
import os
import azext_aro.vendored_sdks.azure.mgmt.redhatopenshift.v2019_12_31_preview.models as v2019_12_31_preview
from azext_aro._aad import AADManager
from azext_aro._rbac import assign_contributor_to_vnet
from azext_aro._validators import validate_subnets
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.util import sdk_no_wait
from msrestazure.azure_exceptions import CloudError
def create_aro(cmd, resource_group_name, aro_name, location=None, tags=None):
raise CLIError('TODO: Implement `aro create`')
FP_CLIENT_ID = "f1dd0a37-89c6-4e07-bcd1-ffd3d43d8875"
def list_aro(cmd, resource_group_name=None):
raise CLIError('TODO: Implement `aro list`')
def aro_create(cmd, # pylint: disable=too-many-locals
client,
resource_group_name,
resource_name,
master_subnet,
worker_subnet,
vnet=None,
vnet_resource_group_name=None, # pylint: disable=unused-argument
location=None,
client_id=None,
client_secret=None,
pod_cidr=None,
service_cidr=None,
master_vm_size=None,
worker_vm_size=None,
worker_vm_disk_size_gb=None,
worker_count=None,
tags=None,
no_wait=False):
vnet = validate_subnets(master_subnet, worker_subnet)
subscription_id = get_subscription_id(cmd.cli_ctx)
aad = AADManager(cmd.cli_ctx)
if client_id is None:
app, client_secret = aad.createManagedApplication(
"aro-%s-%s-%s" % (subscription_id, resource_group_name, resource_name))
client_id = app.app_id
client_sp = aad.getServicePrincipal(client_id)
if not client_sp:
client_sp = aad.createServicePrincipal(client_id)
rp_client_id = FP_CLIENT_ID
if rp_mode_development():
rp_client_id = os.environ['AZURE_FP_CLIENT_ID']
rp_client_sp = aad.getServicePrincipal(rp_client_id)
assign_contributor_to_vnet(cmd.cli_ctx, vnet, client_sp.object_id)
assign_contributor_to_vnet(cmd.cli_ctx, vnet, rp_client_sp.object_id)
oc = v2019_12_31_preview.OpenShiftCluster(
location=location,
tags=tags,
service_principal_profile=v2019_12_31_preview.ServicePrincipalProfile(
client_id=client_id,
client_secret=client_secret,
),
network_profile=v2019_12_31_preview.NetworkProfile(
pod_cidr=pod_cidr or "10.128.0.0/14",
service_cidr=service_cidr or "172.30.0.0/16",
),
master_profile=v2019_12_31_preview.MasterProfile(
vm_size=master_vm_size or "Standard_D8s_v3",
subnet_id=master_subnet,
),
worker_profiles=[
v2019_12_31_preview.WorkerProfile(
name="worker", # TODO: "worker" should not be hard-coded
vm_size=worker_vm_size or "Standard_D2s_v3",
disk_size_gb=worker_vm_disk_size_gb or 128,
subnet_id=worker_subnet,
count=worker_count or 3,
)
]
)
return sdk_no_wait(no_wait, client.create,
resource_group_name=resource_group_name,
resource_name=resource_name,
parameters=oc)
def update_aro(cmd, instance, tags=None):
with cmd.update_context(instance) as c:
c.set_param('tags', tags)
return instance
def aro_delete(cmd, client, resource_group_name, resource_name,
no_wait=False):
if not no_wait:
aad = AADManager(cmd.cli_ctx)
try:
oc = client.get(resource_group_name, resource_name)
except CloudError as err:
if err.status_code == 404:
return
raise err
sdk_no_wait(no_wait, client.delete,
resource_group_name=resource_group_name,
resource_name=resource_name)
if not no_wait:
aad.deleteManagedApplication(oc.service_principal_profile.client_id)
def aro_list(client, resource_group_name=None):
if resource_group_name:
return client.list_by_resource_group(resource_group_name).value
return client.list().value
def aro_show(client, resource_group_name, resource_name):
return client.get(resource_group_name, resource_name)
def aro_get_credentials(client, resource_group_name, resource_name):
return client.get_credentials(resource_group_name, resource_name)
def aro_update(client, resource_group_name, resource_name,
worker_count=None,
no_wait=False):
oc = client.get(resource_group_name, resource_name)
if worker_count is not None:
# TODO: [0] should not be hard-coded
oc.worker_profiles[0].count = worker_count
return sdk_no_wait(no_wait, client.create,
resource_group_name=resource_group_name,
resource_name=resource_name,
parameters=oc)
def rp_mode_development():
return os.environ.get('RP_MODE', '').lower() == 'development'

Просмотреть файл

@ -2,17 +2,16 @@ import os
import unittest
from azure_devtools.scenario_tests import AllowLargeResponse
from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer)
from azure.cli.testsdk import ResourceGroupPreparer
from azure.cli.testsdk import ScenarioTest
TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..'))
class AroScenarioTest(ScenarioTest):
@ResourceGroupPreparer(name_prefix='cli_test_aro')
def test_aro(self, resource_group):
self.kwargs.update({
'name': 'test1'
})
@ -21,15 +20,19 @@ class AroScenarioTest(ScenarioTest):
self.check('tags.foo', 'doo'),
self.check('name', '{name}')
])
self.cmd('aro update -g {rg} -n {name} --tags foo=boo', checks=[
self.check('tags.foo', 'boo')
])
count = len(self.cmd('aro list').get_output_in_json())
self.cmd('aro show - {rg} -n {name}', checks=[
self.cmd('aro show -g {rg} -n {name}', checks=[
self.check('name', '{name}'),
self.check('resourceGroup', '{rg}'),
self.check('tags.foo', 'boo')
])
self.cmd('aro delete -g {rg} -n {name}')
final_count = len(self.cmd('aro list').get_output_in_json())
self.assertTrue(final_count, count - 1)

Просмотреть файл

@ -0,0 +1 @@
../../../client

Просмотреть файл

@ -8,8 +8,6 @@ except ImportError:
from distutils import log as logger
logger.warn("Wheel is not available, disabling bdist_wheel hook")
# TODO: Confirm this is the right version number you want and it matches your
# HISTORY.rst entry.
VERSION = '0.1.0'
# The full list of classifiers is available at
@ -28,7 +26,6 @@ CLASSIFIERS = [
'License :: OSI Approved :: Apache Software License',
]
# TODO: Add any additional SDK dependencies here
DEPENDENCIES = [
'azure-cli-core'
]
@ -41,12 +38,10 @@ with open('HISTORY.rst', 'r', encoding='utf-8') as f:
setup(
name='aro',
version=VERSION,
description='Microsoft Azure Command-Line Tools Aro Extension',
# TODO: Update author and email, if applicable
author='Microsoft Corporation',
author_email='azpycli@microsoft.com',
# TODO: consider pointing directly to your source code instead of the generic repo
url='https://github.com/Azure/azure-cli-extensions',
description='Microsoft Azure Command-Line Tools ARO Extension',
author='Red Hat, Inc.',
author_email='support@redhat.com',
url='https://github.com/jim-minter/rp',
long_description=README + '\n\n' + HISTORY,
license='Apache',
classifiers=CLASSIFIERS,

Просмотреть файл