Skip to content

Commit

Permalink
Implement ARO extension
Browse files Browse the repository at this point in the history
Co-authored-by: Mangirdas Judeikis <[email protected]>
  • Loading branch information
Jim Minter and mjudeikis committed Dec 16, 2019
1 parent 972eb70 commit a3096fe
Show file tree
Hide file tree
Showing 23 changed files with 619 additions and 163 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PYTHONPATH=python/az/aro
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: pull_request-test
name: pull_request-test-go
on:
pull_request:
types:
Expand All @@ -21,5 +21,5 @@ jobs:
- name: Test
run: |
set -x
make test
make test-go
[[ -z "$(git status -s)" ]]
27 changes: 27 additions & 0 deletions .github/workflows/pull_request-test-python.yaml
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -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
22 changes: 19 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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
42 changes: 20 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand All @@ -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)

Expand Down Expand Up @@ -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:
Expand All @@ -177,45 +175,45 @@ 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:

```
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"
Expand Down
9 changes: 3 additions & 6 deletions docs/deploy-production-cluster.md
Original file line number Diff line number Diff line change
@@ -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 changes: 0 additions & 4 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>'
29 changes: 0 additions & 29 deletions examples/cluster-v20191231.json

This file was deleted.

18 changes: 7 additions & 11 deletions python/az/aro/azext_aro/__init__.py
Original file line number Diff line number Diff line change
@@ -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)


Expand Down
68 changes: 68 additions & 0 deletions python/az/aro/azext_aro/_aad.py
Original file line number Diff line number Diff line change
@@ -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,
))
Loading

0 comments on commit a3096fe

Please sign in to comment.