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: on:
pull_request: pull_request:
types: types:
@ -21,5 +21,5 @@ jobs:
- name: Test - name: Test
run: | run: |
set -x set -x
make test make test-go
[[ -z "$(git status -s)" ]] [[ -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 /*.crt
/*.key /*.key
/*.pem
/*.kubeconfig /*.kubeconfig
/*.pem
/env* /env*
!/env.example !/env.example
/id_rsa /id_rsa
/pyenv*
/python/az/aro/build
/python/az/aro/dist
/rp /rp
/secrets /secrets

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

@ -3,8 +3,13 @@ COMMIT = $(shell git rev-parse --short HEAD)$(shell [[ $$(git status --porcelain
rp: generate rp: generate
go build -ldflags "-X main.gitCommit=$(COMMIT)" ./cmd/rp go build -ldflags "-X main.gitCommit=$(COMMIT)" ./cmd/rp
az:
cd python/az/aro && python ./setup.py bdist_egg
clean: 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 client: generate
rm -rf pkg/client python/client rm -rf pkg/client python/client
@ -33,6 +38,7 @@ client: generate
sudo chown -R $(USER):$(USER) pkg/client python/client sudo chown -R $(USER):$(USER) pkg/client python/client
rm -rf python/client/azure/mgmt/redhatopenshift/v2019_12_31_preview/aio 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 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: secrets-update:
oc create secret generic aro-v4-dev --from-file=secrets --dry-run -o yaml | oc apply -f - oc create secret generic aro-v4-dev --from-file=secrets --dry-run -o yaml | oc apply -f -
test: generate test-go: generate
go build ./... go build ./...
gofmt -s -w cmd hack pkg gofmt -s -w cmd hack pkg
@ -63,4 +69,14 @@ test: generate
go vet ./... go vet ./...
go test ./... 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 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 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` subscription. *RH ARO engineering*: use the `osadev.cloud` zone in the `dns`
resource group. resource group.
@ -62,15 +73,6 @@
*RH ARO engineering*: use the `localhost` key and certificate in the shared *RH ARO engineering*: use the `localhost` key and certificate in the shared
`secrets/localhost.pem` file. `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 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. 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_FP_CLIENT_ID: RP "first party" application client UUID
* AZURE_CLIENT_ID: RP AAD application client UUID * AZURE_CLIENT_ID: RP AAD application client UUID
* AZURE_CLIENT_SECRET: RP AAD application client secret * 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) * 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 ## Useful commands
``` ```
export VNET_RESOURCEGROUP="$RESOURCEGROUP-vnet" VNET_RESOURCEGROUP="$RESOURCEGROUP-vnet"
az group create -g "$VNET_RESOURCEGROUP" -l "$LOCATION" 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 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: * 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-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 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: * 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: * 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: * 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: * 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: * Scale a cluster:
@ -209,13 +207,13 @@ curl -k "https://localhost:8443/subscriptions/$AZURE_SUBSCRIPTION_ID/providers/M
``` ```
COUNT=4 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: * 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-master"
az network vnet subnet delete -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-worker" az network vnet subnet delete -g "$VNET_RESOURCEGROUP" --vnet-name vnet -n "$CLUSTER-worker"

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

@ -1,14 +1,11 @@
# Deploy production cluster # Deploy production cluster
export VNET_RESOURCEGROUP=$RESOURCEGROUP-vnet VNET_RESOURCEGROUP=$RESOURCEGROUP-vnet
CLUSTER=cluster
export CLUSTER=cluster
az group create -g "$VNET_RESOURCEGROUP" -l "$LOCATION" 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 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-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 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 -g "$RESOURCEGROUP" -n "$CLUSTER" --vnet-resource-group "$VNET_RESOURCEGROUP" --vnet vnet --master-subnet "$CLUSTER-master" --worker-subnet "$CLUSTER-worker"
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

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

@ -4,8 +4,6 @@ export RP_MODE=development
# RH ARO engineering: uncomment the following stanza only and run `make secrets` # RH ARO engineering: uncomment the following stanza only and run `make secrets`
#. secrets/env #. 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 # non-RH ARO engineering: uncomment from here
#export AZURE_TENANT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx> #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_FP_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
#export AZURE_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx> #export AZURE_CLIENT_ID=<xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx>
#export AZURE_CLIENT_SECRET=<secret> #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>' #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 azure.cli.core import AzCommandsLoader
from azure.cli.core.commands import CliCommandType
from azext_aro._help import helps # pylint: disable=unused-import
class AroCommandsLoader(AzCommandsLoader): class AroCommandsLoader(AzCommandsLoader):
def __init__(self, cli_ctx=None): def __init__(self, cli_ctx=None):
from azure.cli.core.commands import CliCommandType aro_custom = CliCommandType(operations_tmpl='azext_aro.custom#{}',
from azext_aro._client_factory import cf_aro client_factory=cf_aro)
aro_custom = CliCommandType(
operations_tmpl='azext_aro.custom#{}',
client_factory=cf_aro)
super(AroCommandsLoader, self).__init__(cli_ctx=cli_ctx, super(AroCommandsLoader, self).__init__(cli_ctx=cli_ctx,
custom_command_type=aro_custom) custom_command_type=aro_custom)
def load_command_table(self, args): def load_command_table(self, args):
from azext_aro.commands import load_command_table
load_command_table(self, args) load_command_table(self, args)
return self.command_table return self.command_table
def load_arguments(self, command): def load_arguments(self, command):
from azext_aro._params import load_arguments
load_arguments(self, command) 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 from azext_aro.custom import rp_mode_development
# TODO: Replace CONTOSO with the appropriate label and uncomment from azext_aro.vendored_sdks.azure.mgmt.redhatopenshift.v2019_12_31_preview import AzureRedHatOpenShiftClient
# from azure.mgmt.CONTOSO import CONTOSOManagementClient from azure.cli.core.commands.client_factory import get_mgmt_service_client
# return get_mgmt_service_client(cli_ctx, CONTOSOManagementClient)
return None
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
from knack.help_files import helps # pylint: disable=unused-import
helps['aro'] = """ helps['aro'] = """
type: group type: group
short-summary: Commands to manage Aros. short-summary: Manage Azure Red Hat OpenShift clusters.
""" """
helps['aro create'] = """ helps['aro create'] = """
type: command type: command
short-summary: Create a Aro. short-summary: Create a cluster.
""" """
helps['aro list'] = """ helps['aro list'] = """
type: command type: command
short-summary: List Aros. short-summary: List clusters.
""" """
# helps['aro delete'] = """ helps['aro delete'] = """
# type: command type: command
# short-summary: Delete a Aro. short-summary: Delete a cluster.
# """ """
# helps['aro show'] = """ helps['aro show'] = """
# type: command type: command
# short-summary: Show details of a Aro. short-summary: Get the details of a cluster.
# """ """
# helps['aro update'] = """ helps['aro update'] = """
# type: command type: command
# short-summary: Update a Aro. 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 azext_aro._validators import validate_cidr
from azext_aro._validators import validate_client_id
from knack.arguments import CLIArgumentType 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, _): 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: with self.argument_context('aro') as c:
c.argument('tags', tags_type) c.argument('location',
c.argument('location', validator=get_default_location_from_resource_group) validator=get_default_location_from_resource_group)
c.argument('aro_name', aro_name_type, options_list=['--name', '-n']) 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('client_id',
c.argument('aro_name', aro_name_type, id_part=None) 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): import ipaddress
# Example of a storage account name or ID validator. import uuid
# 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 azure.cli.core.commands.client_factory import get_mgmt_service_client
from msrestazure.tools import is_valid_resource_id, resource_id from azure.cli.core.commands.client_factory import get_subscription_id
if namespace.storage_account: from azure.cli.core.profiles import ResourceType
if not is_valid_resource_id(namespace.RESOURCE): from knack.util import CLIError
namespace.storage_account = resource_id( 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), subscription=get_subscription_id(cmd.cli_ctx),
resource_group=namespace.resource_group_name, resource_group=namespace.vnet_resource_group_name,
namespace='Microsoft.Storage', namespace='Microsoft.Network',
type='storageAccounts', type='virtualNetworks',
name=namespace.storage_account 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 azure.cli.core.commands import CliCommandType
from azext_aro._client_factory import cf_aro 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, _): 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 with self.command_group('aro', aro_sdk, client_factory=cf_aro, is_preview=True) as g:
# aro_sdk = CliCommandType( g.custom_command('create', 'aro_create', supports_no_wait=True)
# operations_tmpl='<PATH>.operations#None.{}', g.custom_command('delete', 'aro_delete', supports_no_wait=True)
# client_factory=cf_aro) 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)
g.custom_command('get-credentials', 'aro_get_credentials')
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

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

@ -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): FP_CLIENT_ID = "f1dd0a37-89c6-4e07-bcd1-ffd3d43d8875"
raise CLIError('TODO: Implement `aro create`')
def list_aro(cmd, resource_group_name=None): def aro_create(cmd, # pylint: disable=too-many-locals
raise CLIError('TODO: Implement `aro list`') 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): def aro_delete(cmd, client, resource_group_name, resource_name,
with cmd.update_context(instance) as c: no_wait=False):
c.set_param('tags', tags) if not no_wait:
return instance 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 import unittest
from azure_devtools.scenario_tests import AllowLargeResponse 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__), '..')) TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..'))
class AroScenarioTest(ScenarioTest): class AroScenarioTest(ScenarioTest):
@ResourceGroupPreparer(name_prefix='cli_test_aro') @ResourceGroupPreparer(name_prefix='cli_test_aro')
def test_aro(self, resource_group): def test_aro(self, resource_group):
self.kwargs.update({ self.kwargs.update({
'name': 'test1' 'name': 'test1'
}) })
@ -21,15 +20,19 @@ class AroScenarioTest(ScenarioTest):
self.check('tags.foo', 'doo'), self.check('tags.foo', 'doo'),
self.check('name', '{name}') self.check('name', '{name}')
]) ])
self.cmd('aro update -g {rg} -n {name} --tags foo=boo', checks=[ self.cmd('aro update -g {rg} -n {name} --tags foo=boo', checks=[
self.check('tags.foo', 'boo') self.check('tags.foo', 'boo')
]) ])
count = len(self.cmd('aro list').get_output_in_json()) 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('name', '{name}'),
self.check('resourceGroup', '{rg}'), self.check('resourceGroup', '{rg}'),
self.check('tags.foo', 'boo') self.check('tags.foo', 'boo')
]) ])
self.cmd('aro delete -g {rg} -n {name}') self.cmd('aro delete -g {rg} -n {name}')
final_count = len(self.cmd('aro list').get_output_in_json()) final_count = len(self.cmd('aro list').get_output_in_json())
self.assertTrue(final_count, count - 1) self.assertTrue(final_count, count - 1)

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

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

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

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

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