From 6e384fe2f211f9e309f6cee58c6dcb839fcc0271 Mon Sep 17 00:00:00 2001 From: Matthew Christopher Date: Mon, 11 Mar 2024 11:53:46 -0700 Subject: [PATCH] Add ASOv1 -> ASOv2 migration guide Fixes #3006 --- .../guide/asov1-asov2-migration/_index.md | 338 ++++++++++++++++++ .../guide/asov1-asov2-migration/azuresql.md | 57 +++ .../asov1-asov2-migration/databaseusers.md | 39 ++ .../guide/asov1-asov2-migration/eventhub.md | 24 ++ .../guide/asov1-asov2-migration/mysql.md | 40 +++ .../guide/asov1-asov2-migration/postgresql.md | 23 ++ .../guide/asov1-asov2-migration/redis.md | 35 ++ .../guide/asov1-asov2-migration/storage.md | 46 +++ .../asov1-asov2-migration/storagecontainer.md | 5 + docs/hugo/content/guide/uninstalling.md | 1 + docs/hugo/content/tools/asoctl.md | 9 +- 11 files changed, 615 insertions(+), 2 deletions(-) create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/_index.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/azuresql.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/databaseusers.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/eventhub.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/mysql.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/postgresql.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/redis.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/storage.md create mode 100644 docs/hugo/content/guide/asov1-asov2-migration/storagecontainer.md diff --git a/docs/hugo/content/guide/asov1-asov2-migration/_index.md b/docs/hugo/content/guide/asov1-asov2-migration/_index.md new file mode 100644 index 00000000000..283e84a2e19 --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/_index.md @@ -0,0 +1,338 @@ +--- +title: ASOv1 to ASOv2 migration guide +weight: 5 +layout: single +cascade: + - type: docs + - render: always +--- +ASOv1 is [deprecated](https://github.com/Azure/azure-service-operator/blob/main/README.md#aso-v1). +We strongly recommend migrating to ASOv2 to continue getting new features and support. + +## Assumptions + +- Migrating a single cluster. + - If you have ASOv1 installed on multiple clusters, apply these steps to each cluster. +- ASOv1 is installed in its default namespace `azureoperator-system`. + - If it is installed into a different namespace, replace all occurrences of `azureoperator-system` in this document with + the namespace it's installed into. +- ASOv2 is not yet installed in the cluster. +- ASOv1 is managing a single subscription, so ASOv2 will also be managing that same single subscription via a single + global credential. + - See [ASOv2 credential scope](../authentication/#credential-scope) for other credential scopes that allow for + managing multiple subscriptions and/or using multiple identities. + +## Performing the migration + +### Check the version of cert-manager + +Ensure the version of cert-manager in your cluster supports `v1` resources. + +``` +kubectl api-resources --api-group cert-manager.io` +``` +should show `v1` resources. + +> ℹ️ **Note:** We strongly recommend ensuring that you're running the latest version of cert-manager +> (1.14.4 at the time this article was written). + +> ℹ️ **Note:** We also recommend ensuring that ASOv1 is configured to use `cert-manager.io/v1` resources. +> You can run `helm upgrade` and pass `--set certManagerResourcesAPIVersion=cert-manager.io/v1` to ensure ASOv1 is using +> the v1 versions of the cert-manager resources. + +### Install ASOv2 + +Follow the [standard instructions](../../#installation). We recommend you use the same credentials as ASOv1 is currently using. + +> ℹ️ **Note:** Make sure to follow the [guidelines](../crd-management/) for setting the `--crdPattern`. Configure ASOv2 +> to only manage the CRDs which you need. +> +> For example: if you are using storage accounts, resource groups, redis cache's, cosmosdbs, eventhubs, and SQL Azure, +> then you would configure +> `--set crdPattern='resources.azure.com/*;cache.azure.com/*;documentdb.azure.com/*;eventhub.azure.com/*;sql.azure.com/*;storage.azure.com/*'` + +### Stop ASOv1 reconciliation + +Mark each ASOv1 resource with the `skipreconcile=true` annotation. +This annotation ensures that ASOv1 will no longer update or delete the resource in question in Azure. + +To annotate a specific resource: +``` +kubectl annotate -n storageaccount skipreconcile=true +``` + +To annotate all ASOv1 resources in a given namespace: +``` +kubectl annotate $(kubectl api-resources -o name | grep azure.microsoft.com | paste -sd "," - | xargs kubectl get -A -o name) -n skipreconcile=true` +``` + +Once you have annotated the resources, double-check that they all have the `skipreconcile` annotation. + +> ⚠️ **Important**: If you delete an ASOv1 resource that does not have this annotation it will delete the underlying +> Azure resource which may cause downtime or an outage. + +### Use `asoctl` to import the resources from Azure into ASOv2 + +The commandline tool `asoctl` is a utility for working with ASO. Here we are going to use it to import your existing +Azure resources into ASOv2. + +Download the latest version of [asoctl](../../tools/asoctl/). + +Export the resources you want to transfer using the `asoctl import azure-resource` command. `import azure-resource` +imports the referenced resource and all its child resources by default. This means if you want to import the resources +from a specific resource group being managed by ASOv1 you can just refer to the resource group. + +When running this command it's recommended you use at least asoctl v2.7.0 and pass the +`-namespace` (or `-n`) and `-annotation` (or `-a`) arguments to import the resources to a particular namespace with +the `serviceoperator.azure.com/reconcile-policy: skip` annotation. +``` +asoctl import azure-resource /subscriptions//resourceGroups/ -o resources.yaml -namespace -annotation serviceoperator.azure.com/reconcile-policy=skip +``` + +If you have a large number of resources you may want to also use the `--output-folder` option to render a single +resource per file, rather than one file with all resources. + +This should produce a file similar to the one shown here (this is a simplified file showing just a resource group and +a single resource for the sake of brevity. It's likely that your file is much larger): + +```yaml +--- +apiVersion: resources.azure.com/v1api20200601 +kind: ResourceGroup +metadata: + name: aso-test + namespace: ns1 + annotations: + serviceoperator.azure.com/reconcile-policy: skip +spec: + azureName: aso-test + location: westus +--- +apiVersion: storage.azure.com/v1api20230101 +kind: StorageAccount +metadata: + name: aso-test-storage1 + namespace: ns1 + annotations: + serviceoperator.azure.com/reconcile-policy: skip +spec: + accessTier: Hot + allowBlobPublicAccess: false + allowCrossTenantReplication: false + azureName: asoteststorage1 + encryption: + keySource: Microsoft.Storage + services: + blob: + enabled: true + keyType: Account + file: + enabled: true + keyType: Account + kind: StorageV2 + location: westus + minimumTlsVersion: TLS1_0 + networkAcls: + bypass: AzureServices + defaultAction: Allow + owner: + name: aso-test + sku: + name: Standard_RAGRS + tier: Standard + supportsHttpsTrafficOnly: true +``` + +### Examine `resources.yaml` to ensure it has the resources you expect + +Once you have `resources.yaml` locally, examine it to ensure that it has the resources you expect. + +> ℹ️ **Note**: `asoctl` is importing the resources from Azure. This means it will not preserve any Kubernetes labels/annotations +> you have on your ASOv1 resources. If your resources need certain labels or annotations, add those to `resources.yaml` +> manually or use the `--annotation` and `--label` arguments to `asoctl`. + +> ℹ️ **Note**: By default `asoctl` imports everything into the `default` namespace. Before applying any YAML +> created by `asoctl`, ensure that the namespaces for the resources are correct. You can do this by manually +> modifying the YAML, using a tool such as Kustomize, or using the `--namespace` argument to `asoctl`. + +### Configure `resources.yaml` to export the secrets you need from Azure + +These are secrets such as storage account keys which ASOv1 has automatically exported into a Kubernetes secret. + +ASOv1 exports secrets from Azure according to the +[Secret naming](https://github.com/Azure/azure-service-operator/blob/main/docs/v1/howto/secrets.md#secret-naming) +rules. ASOv1 always exports these secrets, there is no user configuration required on either the name of the secret or its values. +In contrast, ASOv2 requires the user to opt-in to secret export, via the `spec.operatorSpec.secrets` property. + +> ℹ️ **Note**: ASOv2 also splits secrets/configuration into two categories: Secrets provided by you, and secrets generated +> by Azure. Exporting secrets from Azure is done via the `spec.operatorSpec.secrets`, while supplying secrets to Azure +> is done by passing the reference to a secret key/value, such as via the +> [administratorLoginPassword field of Azure SQL](../../reference/sql/v1api20211101#sql.azure.com/v1api20211101.Server_Spec). +> This means that there may be cases where a single secret in ASOv1 becomes 2 secrets in ASOv2, one for inputs and one for outputs. + +You may need to configure `resouces.yaml` to export corresponding secrets. To determine if, for a given namespace, +there are secrets being written by ASOv1 and consumed by your applications, check the following two things: + +1. Are there secrets in the namespace owned by ASO? + - Use `kubectl get secrets -n ns1 -o json | jq -r '.items[] | select(.metadata.ownerReferences[]? | + select(.apiVersion | contains("azure.microsoft.com")))'` + to find ASOv1 owned secrets. +2. Do you have pods or services consuming those secrets? If they aren't being consumed anywhere, you don't + need them and they can be ignored/discarded. + +Here's an example of secrets created by ASOv1 for various resources: +```sh +kubectl get secrets -n ns1 +NAME TYPE DATA AGE +azuresqlserver-azuresql-migration-sample-1 Opaque 5 106m +cosmosdb-cosmosdb-sample-1 Opaque 10 6h42m +eventhub-eventhub-sample-1 Opaque 7 101m +rediscache-rediscache-sample-1 Opaque 2 80m +storageaccount-cutoverteststorage1 Opaque 5 105m +``` + +Secrets owned by ASOv1 will have an `ownerReferences` section like this: +``` + ownerReferences: + - apiVersion: azure.microsoft.com/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: Eventhub + name: eventhub-sample-1 + uid: 0a034f50-3060-4114-98e6-b5085326da0d +``` + +Once you've identified the set of secrets which are exported by ASOv1 and which are being consumed by your applications, +configure ASOv2 to export similar secrets by using the `spec.operatorSpec.secrets` field. See [examples](#examples) for +examples of various resource types. + +> ℹ️ **Note:** It is strongly recommended that you export the ASOv2 secrets to a _different_ secret name than the +> ASOv1 secret. ASOv2 will not allow you to overwrite an existing secret with `spec.operatorSpec.secrets`. +> You'll get an error that looks like +> "cannot overwrite Secret ns1/storageaccount-cutoverteststorage1 which is not owned by StorageAccount.storage.azure.com +> ns1/aso-migration-test-cutoverteststorage1". +> Instead of overwriting the same secret, create a different secret with ASOv2 (recommend including a suffix to +> identify it such as -asov2). Then, when you're ready, swap your deployment to use the new ASOv2 secrets. + +### Configure `resources.yaml` to source secret values from Kubernetes + +Some Azure services require user-supplied secrets, for example: Azure SQL Server requires an administrator password. +ASOv1 generated these passwords for you, but in ASOv2 you control these passwords and their lifecycle (including rollover). + +`asoctl` does not automatically include secret password fields in its output. Instead, you must supply a Kubernetes +secret containing the password which ASOv2 will supply to Azure. + +The following ASOv1 resources automatically generated usernames and passwords which you will need to take ownership of +when migrating to ASOv2: +- Azure SQL Server +- Azure SQL User +- My SQL Server +- My SQL User +- PostgreSQL SQL Server +- PostgreSQL SQL User +- VM +- VMSS + +The process for each of these resources is the same: +1. Find the ASOv1 managed secret which contains the current password. +2. Create a new secret containing the same password. +3. Update the ASOv2 `resources.yaml` to supply the new password in the correct location. + +See [examples](#examples) for examples of how to do this with various resource types. + +### Configure `resources.yaml` to have annotation [serviceoperator.azure.com/reconcile-policy: skip](../annotations/#serviceoperatorazurecomreconcile-policy) + +This can be done automatically by `asoctl` during resource import with the +`-annotation serviceoperator.azure.com/reconcile-policy=skip` argument or manually by modifying `resources.yaml` +afterward. + +> ⚠️ **Important:** Be _very_ careful issuing deletions of ASOv2 resources once you've imported them if they do not have +> the serviceoperator.azure.com/reconcile-policy=skip annotation. Just like ASOv1, +> ASOv2 will delete the underlying Azure resource by default! +> It is _strongly_ recommended that you use asoctl's `-annotation serviceoperator.azure.com/reconcile-policy=skip` flag +> and only remove that annotation a few resources at a time to ensure things are working as you expect. + +### Apply `resources.yaml` to your cluster + +This will create the ASOv2 resources. At this point you should be in a state where: +- ASOv1 is not managing the resources because they have all been annotated with `skipreconcile=true`. +- ASOv2 is not managing the resources because they have all been annotated with `serviceoperator.azure.com/reconcile-policy=skip`. + +Check that the ASOv2 resources are in the correct namespaces and that you're seeing the secrets and configmaps you expect +(these will be exported by ASOv2 even when annotated with `serviceoperator.azure.com/reconcile-policy=skip`). + +Remove the `serviceoperator.azure.com/reconcile-policy=skip` from the ASOv2 resources (one at a time if desired) and swap existing +deployments to source data from the ASOv2 secrets/configmaps. + +## Rolling back + +If at any point something isn't working, you can roll back by marking the ASOv2 resource with the +`serviceoperator.azure.com/reconcile-policy=skip` annotation and removing the `skipreconcile=true` annotation +from the ASOv1 resource. Don't forget to swap your deployments to use the secrets from ASOv1 if they had been updated +to rely on the ASOv2 secrets. + +## Cleaning up + +Once you've migrated the ASOv1 resources to ASOv2 and been running successfully for a while, you can delete the ASOv1 resources +in Kubernetes with `kubectl delete`. + +> ⚠️ **Important:** Make sure that each ASOv1 resource you delete has the `skipreconcile=true` annotation before you delete it +> in Kubernetes or else the deletion will propagate to Azure and delete the underlying Azure resource as well, which you do not want. + +## Examples + +- [Storage](./storage/) +- [Storage Container](./storagecontainer/) +- [EventHub](./eventhub/) +- [Azure SQL](./azuresql/) +- [Redis](./redis/) +- [MySQL](./mysql/) +- [PostgreSQL](./postgresql/) +- [Database users](./databaseusers/) + +## Gotchas + +- `resourcegroup` is the Kubernetes "short name" for both ASOv1 and ASOv2 resource groups. This means that running + `kubectl get resourcegroups` is ambiguous. Kubernetes will use ASOv1's `resourcegroup` asuming it was installed first. + If ASOv1 and ASOv2 are installed into the same cluster, we recommend using the fully specified name for querying + resourcegroups from ASOv2: `kubectl get resourcegroup.resources.azure.com`. +- ASOv2 does not support keyVault secrets. TODO: Alternatives? +- ASOv2 does not support MySQL single server or PostgreSQL single server, as these are being deprecated. + See [MySQL](./mysql/) and [PostgreSQL](./postgresql/) for more details. + +## Helpful commands + +View all ASOv1 resources in the cluster: +```sh +kubectl api-resources -o name | grep azure.microsoft.com | paste -sd "," - | xargs kubectl get -A +``` + +View all ASOv2 resources in the cluster: +```sh +kubectl api-resources -o name | grep azure.com | paste -sd "," - | xargs kubectl get -A +``` + +Annotate a single ASOv1 resource to skip all reconciliation: +```sh +kubectl annotate -n ns1 storageaccount skipreconcile=true +``` + +Annotate existing ASOv1 resources in namespace `ns1` to skip all reconciliation: +```sh +kubectl annotate $(kubectl api-resources -o name | grep azure.microsoft.com | paste -sd "," - | xargs kubectl get -A -o name) -n ns1 skipreconcile=true +``` + +Find secrets owned/created by ASOv1: +```sh +kubectl get secrets -n ns1 -o json | jq -r '.items[] | select(.metadata.ownerReferences[]? | select(.apiVersion | contains("azure.microsoft.com")))' +``` + +Annotate existing ASOv2 resources in namespace `ns1` to skip all reconciliation: +```sh +kubectl annotate $(kubectl api-resources -o name | grep azure.com | paste -sd "," - | xargs kubectl get -A -o name) -n ns1 serviceoperator.azure.com/reconcile-policy=skip +``` + +Clear the `serviceoperator.azure.com/reconcile-policy=skip` annotation: +```sh +kubectl annotate $(kubectl api-resources -o name | grep azure.com | paste -sd "," - | xargs kubectl get -A -o name) -n ns1 serviceoperator.azure.com/reconcile-policy- +``` diff --git a/docs/hugo/content/guide/asov1-asov2-migration/azuresql.md b/docs/hugo/content/guide/asov1-asov2-migration/azuresql.md new file mode 100644 index 00000000000..98f998010ba --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/azuresql.md @@ -0,0 +1,57 @@ +--- +title: Azure SQL +--- +## Secrets + +ASOv1 `AzureSqlServer` creates a Kubernetes secret associated with the SQL server: + +``` +kubectl get secrets -n ns1 + +azuresqlserver-azuresql-migration-sample-1 Opaque 5 22h +``` + +This secret has the following 5 keys: + +| Key | Source | ASOv2 equivalent | +|--------------------------|-----------------|---------------------------------------------------------------------| +| azureSqlServerName | User | None (see `.spec.operatorSpec.configMaps.fullyQualifiedDomainName`) | +| fullyQualifiedServerName | Azure | `.spec.operatorSpec.configMaps.fullyQualifiedDomainName` | +| fullyQualifiedUsername | ASOv1 generated | None | +| password | ASOv1 generated | `.spec.administratorPassword` | +| username | ASOv1 generated | `.spec.administratorLogin` | + +Unlike ASOv1, ASOv2 does not automatically generate any usernames or passwords. It is up to you to manage and generate +usernames and passwords. You can continue using the ASOv1 generated username and password when you switch from ASOv1 to ASOv2, +as shown below. + +Example ASOv2 YAML snippet for exporting FQDN to configmap: +```yaml +spec: + operatorSpec: + configMaps: + fullyQualifiedDomainName: + name: azuresqlserver-azuresql-migration-sample-1-asov2 + key: fullyQualifiedServerName +``` + +Example ASOv2 YAML snippet for configuring the server username and password: + +ASOv2 doesn't classify the administrator account name as a secret, so it will be automatically +included in the YAML after running `asoctl`. + +We recommend making a _new_ secret containing just the server password. You can produce this secret by copying +the value from the ASOv1 secret `password` key into a new secret. In this example we'd create a new secret named +`azuresqlserver-azuresql-migration-sample-admin-pw` containing the `password` key with value from the ASOv1 secret +`azuresqlserver-azuresql-migration-sample-1` `password` key. +```yaml +spec: + administratorLogin: myusername # This value will be automatically populated by asoctl + administratorLoginPassword: + name: azuresqlserver-azuresql-migration-sample-admin-pw + key: password +``` + +Once you've applied the above, make sure to update your applications to depend on the new configmap +written by ASOv2 (`azuresqlserver-azuresql-migration-sample-1-asov2`), and the new secret written by you +(`azuresqlserver-azuresql-migration-sample-admin-pw`). diff --git a/docs/hugo/content/guide/asov1-asov2-migration/databaseusers.md b/docs/hugo/content/guide/asov1-asov2-migration/databaseusers.md new file mode 100644 index 00000000000..fe7fedca7a8 --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/databaseusers.md @@ -0,0 +1,39 @@ +--- +title: Database Users +--- + +ASOv1 supports 3 types of database user: +- Azure SQL +- MySQL +- PostgreSQL + +Unlike other resources, these users do not exist as first class entities in the Azure API. This means +that `asoctl` will not import them. + +To migrate database users from ASOv1 to ASOv2, you must manually transform the ASOv1 DatabaseUser shape +into the ASOv2 shape. + +## Azure SQL + +ASOv1 `AzureSQLUser` [example](https://github.com/Azure/azure-service-operator/blob/main/config/samples/azure_v1alpha1_azuresqluser.yaml). + +ASOv2 `sql.azure.com/User` [example](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/sql/v1api/v1api/v1_user.yaml). + +## MySQL + +ASOv1 `MySQLUser` [example](https://github.com/Azure/azure-service-operator/blob/main/config/samples/azure_v1alpha2_mysqluser.yaml). + +ASOv1 `MySQLAADUser` [example](https://github.com/Azure/azure-service-operator/blob/main/config/samples/azure_v1alpha2_mysqlaaduser.yaml). + +ASOv2 `dbformysql.azure.com/User` [example](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/dbformysql/v1api/v1_user.yaml). + +ASOv2 `dbformysql.azure.com/User` in AAD configuration [example](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/dbformysql/v1api/v1_user_aad.yaml). +- Note that MySQL Flexible server has certain requirements for how it is set up to work with AAD [here](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/dbformysql/v1api/v1_user_aad.yaml#L3). + So not only do you need to create the user in ASOv2 you must also ensure that the FlexibleServer has the right + [FlexibleServerAdministrator](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/dbformysql/v1api/v1api20220101_flexibleserversadministrator.yaml) configured, with the right permissions. + +## PostgreSQL + +ASOv1 `PostgreSQLUser` [example](https://github.com/Azure/azure-service-operator/blob/main/config/samples/azure_v1alpha1_postgresqluser.yaml). + +ASOv2 `dbforpostgresql.azure.com/User` [example](https://github.com/Azure/azure-service-operator/blob/main/v2/samples/dbforpostgresql/v1api/v1_user.yaml). \ No newline at end of file diff --git a/docs/hugo/content/guide/asov1-asov2-migration/eventhub.md b/docs/hugo/content/guide/asov1-asov2-migration/eventhub.md new file mode 100644 index 00000000000..29b93ebc54c --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/eventhub.md @@ -0,0 +1,24 @@ +--- +title: EventHub +--- +## Secrets + +ASOv1 `EventHubs` save a Kubernetes secret: + +``` +kubectl get secrets -n ns1 + +eventhub-eventhub-sample-1 Opaque 7 22h +``` + +This secret has the following 7 keys: + +| Key | Source | ASOv2 equivalent | +|---------------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| eventhubName | User | None | +| eventhubNamespace | User | None | +| primaryConnectionString | Azure | `.spec.operatorSpec.secrets.primaryConnectionString` (added in ASO v2.7.0) | +| primaryKey | Azure | `.spec.operatorSpec.secrets.primaryKey` (added in ASO v2.7.0) | +| secondaryConnectionString | Azure | `.spec.operatorSpec.secrets.secondaryConnectionString` (added in ASO v2.7.0) | +| secondaryKey | Azure | `.spec.operatorSpec.secrets.secondaryKey` (added in ASO v2.7.0) | +| sharedaccessKey | Azure | None, see ASOv2 [NamespacesAuthorizationRules](https://azure.github.io/azure-service-operator/reference/eventhub/v1api20211101/#eventhub.azure.com/v1api20211101.NamespacesAuthorizationRule) for exporting keys for any authorization rule except the default `RootManageSharedAccessKey` | diff --git a/docs/hugo/content/guide/asov1-asov2-migration/mysql.md b/docs/hugo/content/guide/asov1-asov2-migration/mysql.md new file mode 100644 index 00000000000..54709bbcd34 --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/mysql.md @@ -0,0 +1,40 @@ +--- +title: MySQL +--- + +ASOv1 supports MySQL single server, which is +[being deprecated](https://learn.microsoft.com/azure/mysql/single-server/whats-happening-to-mysql-single-server). + +ASOv2 does _not_ support MySQL single server, but it does support MySQL Flexible Server (the single server replacement). + +Migration from ASOv1 to ASOv2 for these resources is a two-step process: +1. Migrate from Single Server to Flexible Server. +2. Import the Flexible Server into ASOv2. + +### Migrate from Single Server to Flexible Server + +Annotate your ASOv1 MySQL single servers in Kubernetes with the `skipreconcile=true` annotation, and then +follow the [migration guide](https://learn.microsoft.com/azure/mysql/single-server/whats-happening-to-mysql-single-server#migrate-from-single-server-to-flexible-server). + +Once the migration has been complete, use [asoctl](../../../tools/asoctl/) to import the new Flexible Server resource from +Azure following the standard process defined in the [ASOv1 to ASOv2 migration guide](../) + +## Secrets + +ASOv1 `MySQLServer` saves a Kubernetes secret: + +``` +kubectl get secrets -n ns1 + +mysqlserver-mysqlserver-migration Opaque 5 17h +``` + +This secret has the following 5 keys: + +| Key | Source | ASOv2 equivalent | +|---------------------------|-----------------|--------------------------------------------------------------------------| +| fullyQualifiedServerName | User | `.spec.operatorSpec.secrets.fullyQualifiedDomainName` | +| fullyQualifiedUsername | User | None | +| mySqlServerName | User | None (see `.spec.operatorSpec.secrets.fullyQualifiedDomainName`) | +| password | ASOv1 generated | `.spec.administratorLoginPassword` | +| username | ASOv1 generated | `.spec.operatorSpec.configMaps.administratorLogin` (added in ASO v2.7.0) | diff --git a/docs/hugo/content/guide/asov1-asov2-migration/postgresql.md b/docs/hugo/content/guide/asov1-asov2-migration/postgresql.md new file mode 100644 index 00000000000..ca75dd861ae --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/postgresql.md @@ -0,0 +1,23 @@ +--- +title: PostgreSQL +--- + +ASOv1 supports PostgreSQL single server, which is +[being deprecated](https://azure.microsoft.com/updates/azure-database-for-postgresql-single-server-will-be-retired-migrate-to-flexible-server-by-28-march-2025/). + +ASOv2 does _not_ support PostgreSQL single server, but it does support PostgreSQL Flexible Server +(the single server replacement). + +Migration from ASOv1 to ASOv2 for these resources is a two-step process: +1. Migrate from Single Server to Flexible Server. +2. Import the Flexible Server into ASOv2. + +### Migrate from Single Server to Flexible Server + +Annotate your ASOv1 PostgreSQL single servers in Kubernetes with the `skipreconcile=true` annotation, +and then follow the +[migration guide](https://learn.microsoft.com/azure/postgresql/migrate/migration-service/concepts-migration-service-postgresql). + +Once the migration has been complete, use [asoctl](../../../tools/asoctl/) to import the new Flexible Server resource from +Azure following the standard process defined in the [ASOv1 to ASOv2 migration guide](../) + diff --git a/docs/hugo/content/guide/asov1-asov2-migration/redis.md b/docs/hugo/content/guide/asov1-asov2-migration/redis.md new file mode 100644 index 00000000000..485c928d5b7 --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/redis.md @@ -0,0 +1,35 @@ +--- +title: Redis +--- +## Secrets + +ASOv1 `RedisCaches` save a Kubernetes secret: + +``` +kubectl get secrets -n ns1 + +rediscache-rediscache-sample-1 Opaque 2 9s +``` + +This secret has the following 2 keys: + +| Key | Source | ASOv2 equivalent | +|-------------------|--------|-------------------------------------------| +| primaryKey | Azure | `.spec.operatorSpec.secrets.primaryKey` | +| secondaryKey | Azure | `.spec.operatorSpec.secrets.secondaryKey` | + +Example ASOv2 YAML snippet: +```yaml +spec: + operatorSpec: + secrets: + primaryKey: + name: rediscache-rediscache-sample-1-asov2 + key: primarykey + key1: + name: rediscache-rediscache-sample-1-asov2 + key: secondaryKey +``` + +Once you've applied the above, make sure to update your applications to depend on the new secret +written by ASOv2. diff --git a/docs/hugo/content/guide/asov1-asov2-migration/storage.md b/docs/hugo/content/guide/asov1-asov2-migration/storage.md new file mode 100644 index 00000000000..a9d96c3c291 --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/storage.md @@ -0,0 +1,46 @@ +--- +title: Storage Account +--- +## Secrets + +ASOv1 `StorageAccounts` save a Kubernetes secret: + +``` +kubectl get secrets -n ns1 + +storageaccount-cutoverteststorage1 Opaque 5 17h +``` + +This secret has the following 5 keys: + +| Key | Source | ASOv2 equivalent | +|--------------------|--------|-----------------------------------------------| +| StorageAccountName | User | None | +| connectionString0 | Azure | None (see `.spec.operatorSpec.secrets.key1`) | +| key0 | Azure | `.spec.operatorSpec.secrets.key1` | +| connectionString1 | Azure | None (see `.spec.operatorSpec.secrets.key2`) | +| key1 | Azure | `.spec.operatorSpec.secrets.key2` | + +Instead of full connection strings, ASOv2 exposes individual endpoints such as `blobEndpoint`, which you can use to +craft a connection string. + +Example ASOv2 YAML snippet: +```yaml +spec: + operatorSpec: + secrets: + blobEndpoint: + name: storageaccount-cutoverteststorage1-asov2 + key: blobEndpoint + key1: + name: storageaccount-cutoverteststorage1-asov2 + key: key0 # Matches the name the ASOv1 generated secret used +``` + +Once you've applied the above, make sure to update your applications to depend on the new secret +written by ASOv2. + +TODO: How to work around connection string issues? +1. init container? https://stackoverflow.com/questions/77748779/how-to-prepare-environment-variables-with-init-containers-in-a-kubernetes-deploy +2. Support https://github.com/Azure/azure-service-operator/issues/3446? +3. Modify the command of the container itself to run a script first? diff --git a/docs/hugo/content/guide/asov1-asov2-migration/storagecontainer.md b/docs/hugo/content/guide/asov1-asov2-migration/storagecontainer.md new file mode 100644 index 00000000000..8732b269ce8 --- /dev/null +++ b/docs/hugo/content/guide/asov1-asov2-migration/storagecontainer.md @@ -0,0 +1,5 @@ +--- +title: Storage Container +--- + +No additional handling is needed above what `asoctl` automatically imports. Just apply the imported resource! diff --git a/docs/hugo/content/guide/uninstalling.md b/docs/hugo/content/guide/uninstalling.md index d9e0b2c4ffc..4a9af24ef55 100644 --- a/docs/hugo/content/guide/uninstalling.md +++ b/docs/hugo/content/guide/uninstalling.md @@ -1,5 +1,6 @@ --- title: "Uninstalling" +weight: 10 --- ## Before you uninstall diff --git a/docs/hugo/content/tools/asoctl.md b/docs/hugo/content/tools/asoctl.md index 5c78661483e..49c92c89180 100644 --- a/docs/hugo/content/tools/asoctl.md +++ b/docs/hugo/content/tools/asoctl.md @@ -166,10 +166,15 @@ Usage: asoctl import azure-resource [flags] Flags: - -h, --help help for azure-resource - -o, --output string Write ARM resource CRD to a file + -a, --annotations strings Add the specified annotations to the imported resources. Multiple comma-separated annotations can be specified (example.com/myannotation=foo,example.com/myannotation2=bar) or the --annotations (-a) argument can be used multiple times (-a example.com/myannotation=foo -a example.com/myannotation2=bar) + -h, --help help for azure-resource + -l, --labels strings Add the specified labels to the imported resources. Multiple comma-separated labels can be specified (example.com/mylabel=foo,example.com/mylabel2=bar) or the --labels (-l) argument can be used multiple times (-l example.com/mylabel=foo -l example.com/mylabel2=bar) + -n, --namespace string Write the imported resources to the specified namespace + -o, --output string Write ARM resource CRDs to a single file + -f, --output-folder string Write ARM resource CRDs to individual files in a folder Global Flags: + --quiet Silence most logging --verbose Enable verbose logging ```