Skip to content

Commit

Permalink
Temporary/sync generation to future #26205 (#26313)
Browse files Browse the repository at this point in the history
* AzConnectedKubernetes fixes and improvements (#26205)

* read value of env:KUBECONFIG

* read value of env:HELMCHART

* other misuages on Env:

* revert path read on string values

* read string value of en vars

* Use Get-AzEnvironment not Get-AzureEnvironment (which doesn't work on Linux).

* Trace the name of the current cloud if in Debug mode.

* Trace the parameters passed to the internal function.

* fix test

* fix test

* remove unrelated test files

* Moving DEVELOP.md to custom so that autorest does not delete it.

* Prefix Env with dollar.

* Pass through debug and verbose flags.

* Revert "Prefix Env with dollar."

This reverts commit e4651f4.

* Ensure correct path to helm.

---------

Co-authored-by: Paul D.Smith <[email protected]>

* check whether assemblyinfo existed before modifying it

* adapt

* Update ChangeLog.md

* Update ConnectedKubernetes.sln

---------

Co-authored-by: xw-zhang24 <[email protected]>
Co-authored-by: Paul D.Smith <[email protected]>
  • Loading branch information
3 people authored Oct 15, 2024
1 parent 5d2661c commit f159ecc
Show file tree
Hide file tree
Showing 22 changed files with 389 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the ""License"");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an ""AS IS"" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by Microsoft (R) AutoRest Code Generator.Changes may cause incorrect behavior and will be lost if the code
// is regenerated.

using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: System.Reflection.AssemblyCompanyAttribute("Microsoft")]
[assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright © Microsoft")]
[assembly: System.Reflection.AssemblyProductAttribute("Microsoft Azure PowerShell")]
[assembly: System.Reflection.AssemblyTitleAttribute("Microsoft Azure PowerShell - ConnectedKubernetes")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("0.11.0")]
[assembly: System.Reflection.AssemblyVersionAttribute("0.11.0")]
[assembly: System.Runtime.InteropServices.ComVisibleAttribute(false)]
[assembly: System.CLSCompliantAttribute(false)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Develpoing ConnectedKubernetes Powershell Cmdlets
> These notes are intended to compliment and extend the common instructions for this process. If you spot a sensible common location where part of this document could live, please do move the information out of here.
# Overview
## Why Custom Cmdlets?
Powerhsll cmdlets can be created almost totally automatically for many products but ConnectedKubernetes is special. The standard cmdlet interations are one or more ([Swagger]) REST API exchanges with Azure but ConnectedKubernetes also has to install Azure Arc support into a Kubernetes cluster and this requires work to be performed using [helm].

For this reason, the ConnectedKubernetes cmdlets have two or more steps such as:
- Interact with Azure using the REST APIs; this often involves just calling the autogenerated cmdlets
- Now interact with Kubernetes using [helm].

## (Part) Autogeneration Process
The autogeneration process uses [autorest.powershell], an [autorest] extension for creating Powershell cmdlets based on a (Swagger) REST API definition. this is typically as follows:

1. Carefully craft your [Swagger] definition of the REST API
1. Read the [Quickstart for Azure PowerShell development using code generator]
1. Clone the [azure-powershell] repo
1. Create a develpoment branch based on the `generate` branch **and not based on `main`**!
1. Run the [autorest] Docker image; if you have no local image for [autorest], refer to
1. Run [autorest] to generate configuration and files that will result in the autogenerated cmdlets
1. Run the build process (`pwsh build-module.ps1`) which completes the build process.

### Building the [autorest] Docker image
> Do **NOT** build an [autorest] image based on the Dockerfile contained in the `tools/autorest` directory below the [azure-powershell] repo as this does not produce a working image!
- Clone the [autorest.powershell] repo
- Navigate to the `tools/docker` directory
- Follow the instructions in the README file in that directory

## Special Aspects for ConnectedKubernetes
The autogenerated cmdlets are created in C# with Powershell wrappers that are placed into the `internal` folder. This is because we are **NOT** exposing the autogenerated functions to the user, rather er export our custom versions.
> As described earlier, the custom versions often call-through to the autogenerated version to perform the ARM REST API portion of their work.
### Gotchas
#### You Want a New Cmdlet?
If you are creating a whole new command, then you need to get the [autorest] process and the build process to work together to create the underlying `internal` command for you and this is not trivial.

When we tried to add the `Set-` cmdlet, we found it never appeared but eventually we discovered these nuggets of knowledge.
- [autorest] will look at the `operationId` field in the [Swagger] for each REST API method and determine what commands to create. So in our case `ConnectedCluster_Create` only causes `New-` cmdlets to be created and we had to update the [Swagger] to say `ConnectedCluster_CreateOrUpdate` before any `Set-` cmdlets were created
- The `internal` cmdlets are really just Powershell wrappers but these are not created until the `pwsh build-module-ps1` step
- Between the steps above sits the [autorest] configuration found in the XML at the end of [README.md]. This does stuff like:
- Stops the generation of various versions of cmdlets that are not required
- **hides** the autogenerated cmdlets, which is what causes them to be created in `internal`; we had to add `set` to the list of cmdlets so hidden before the `internal` `Set-` cmdlet appeared.

[autorest.powershell]: https://github.com/Azure/autorest.powershell
[autorest]: https://github.com/Azure/autorest
[helm]: https://helm.sh/
[Swagger]: https://swagger.io/
[README.md]: ./README.md
[Quickstart for Azure PowerShell development using code generator]: https://eng.ms/docs/cloud-ai-platform/azure-core/azure-management-and-platforms/control-plane-bburns/azure-cli-tools-azure-cli-powershell-and-terraform/azure-cli-tools/onboarding/azurepowershell/quickstart_codegen
[azure-powershell]: https://github.com/azure/azure-powershell
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ function New-AzConnectedKubernetes {
$Null = $PSBoundParameters.Remove('KubeConfig')
}
elseif (Test-Path Env:KUBECONFIG) {
$KubeConfig = Get-ChildItem -Path Env:KUBECONFIG
$KubeConfig = Get-ChildItem -Path $Env:KUBECONFIG
}
elseif (Test-Path Env:Home) {
$KubeConfig = Join-Path -Path $Env:Home -ChildPath '.kube' | Join-Path -ChildPath 'config'
Expand Down Expand Up @@ -364,7 +364,11 @@ function New-AzConnectedKubernetes {
$PSBoundParameters.Add('IdentityType', $IdentityType)

#Region check helm install
Confirm-HelmVersion -KubeConfig $KubeConfig
Confirm-HelmVersion `
-KubeConfig $KubeConfig `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)


#EndRegion
$helmClientLocation = 'helm'
Expand Down Expand Up @@ -421,8 +425,13 @@ function New-AzConnectedKubernetes {
if ($PSVersionTable.PSVersion.Major -eq 5) {
try {
. "$PSScriptRoot/helpers/RSAHelper.ps1"
$AgentPublicKey = ExportRSAPublicKeyBase64($RSA)
$AgentPrivateKey = ExportRSAPrivateKeyBase64($RSA)
$AgentPublicKey = ExportRSAPublicKeyBase64($RSA) `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)
$AgentPrivateKey = ExportRSAPrivateKeyBase64($RSA) `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

$AgentPrivateKey = "-----BEGIN RSA PRIVATE KEY-----`n" + $AgentPrivateKey + "`n-----END RSA PRIVATE KEY-----"
}
catch {
Expand Down Expand Up @@ -541,15 +550,25 @@ function New-AzConnectedKubernetes {

# A lot of what follows relies on knowing the cloud we are using and the
# various endpoints so get that information now.
$cloudMetadata = Get-AzCloudMetadata
$cloudMetadata = Get-AzCloudMetadata `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

# Perform DP health check
$configDpinfo = Get-ConfigDPEndpoint -location $Location -Cloud $cloudMetadata
$configDpinfo = Get-ConfigDPEndpoint `
-location $Location `
-Cloud $cloudMetadata `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

$configDPEndpoint = $configDpInfo.configDPEndpoint

# If the health check fails (not 200 response), an exception is thrown
# so we can ignore the output.
$null = Invoke-ConfigDPHealthCheck -configDPEndpoint $configDPEndpoint
$null = Invoke-ConfigDPHealthCheck `
-configDPEndpoint $configDPEndpoint `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

# This call does the "pure ARM" update of the ARM objects.
Write-Debug "Writing Connected Kubernetes ARM objects."
Expand Down Expand Up @@ -586,7 +605,12 @@ function New-AzConnectedKubernetes {
# needs to change and not the Powershell script (or az CLI).
#
# Do not send protected settings to CCRP
$arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration -ConfigurationSetting $ConfigurationSetting -RedactedProtectedConfiguration @{} -CCRP $true
$arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration `
-ConfigurationSetting $ConfigurationSetting `
-RedactedProtectedConfiguration @{} `
-CCRP $true `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

# It is possible to set an empty value for these parameters and then
# the code above gets skipped but we still need to remove the empty
Expand All @@ -601,14 +625,20 @@ function New-AzConnectedKubernetes {
$PSBoundParameters.Add('ArcAgentryConfiguration', $arcAgentryConfigs)

Write-Output "Creating 'Kubernetes - Azure Arc' object in Azure"
Write-Debug "PSBoundParameters: $PSBoundParameters"
$Response = Az.ConnectedKubernetes.internal\New-AzConnectedKubernetes @PSBoundParameters

if ((-not $WhatIfPreference) -and (-not $Response)) {
Write-Error "Failed to create the 'Kubernetes - Azure Arc' resource."
return
}

$arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration -ConfigurationSetting $ConfigurationSetting -RedactedProtectedConfiguration $RedactedProtectedConfiguration -CCRP $false
$arcAgentryConfigs = ConvertTo-ArcAgentryConfiguration `
-ConfigurationSetting $ConfigurationSetting `
-RedactedProtectedConfiguration $RedactedProtectedConfiguration `
-CCRP $false `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

# Convert the $Response object into a nested hashtable.

Expand Down Expand Up @@ -643,7 +673,7 @@ function New-AzConnectedKubernetes {
Write-Debug "OCI Artifact location: ${helmValuesDp.repositoryPath}."

$registryPath = if ($env:HELMREGISTRY) { $env:HELMREGISTRY } else { $helmValuesDp.repositoryPath }
Write-Debug "RegistryPath: ${registryPath}."
Write-Debug "RegistryPath: ${registryPath}"

$helmValuesContent = $helmValuesDp.helmValuesContent
Write-Debug "Helm values: ${helmValuesContent}."
Expand All @@ -654,7 +684,7 @@ function New-AzConnectedKubernetes {
# hashtable.
$optionsFromDp = ""
foreach ($field in $helmValuesContent.PSObject.Properties) {
if($field.Value.StartsWith($ProtectedSettingsPlaceholderValue)){
if ($field.Value.StartsWith($ProtectedSettingsPlaceholderValue)) {
$parsedValue = $field.Value.Split(":")
# "${ProtectedSettingsPlaceholderValue}:${feature}:${setting}"
$field.Value = $ConfigurationProtectedSetting[$parsedValue[1]][$parsedValue[2]]
Expand All @@ -670,9 +700,15 @@ function New-AzConnectedKubernetes {

# Get helm chart path (within the OCI registry).
if ($PSCmdlet.ShouldProcess("configDP", "request Helm chart")) {
$chartPath = Get-HelmChartPath -registryPath $registryPath -kubeConfig $KubeConfig -kubeContext $KubeContext -helmClientLocation $HelmClientLocation
$chartPath = Get-HelmChartPath `
-registryPath $registryPath `
-kubeConfig $KubeConfig `
-kubeContext $KubeContext `
-helmClientLocation $HelmClientLocation `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)
if (Test-Path Env:HELMCHART) {
$ChartPath = Get-ChildItem -Path Env:HELMCHART
$ChartPath = Get-ChildItem -Path $Env:HELMCHART
}
}

Expand Down Expand Up @@ -722,9 +758,11 @@ function New-AzConnectedKubernetes {

if ($ExistConnectedKubernetes.ArcAgentProfileAgentState -eq "Succeeded") {
Write-Output "Cluster configuration succeeded."
} elseif ($ExistConnectedKubernetes.ArcAgentProfileAgentState -eq "Failed") {
}
elseif ($ExistConnectedKubernetes.ArcAgentProfileAgentState -eq "Failed") {
Write-Error "Cluster configuration failed."
} else {
}
else {
Write-Error "Cluster configuration timed out after 60 minutes."
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ param(
if ($PSBoundParameters.ContainsKey('KubeConfig')) {
$Null = $PSBoundParameters.Remove('KubeConfig')
} elseif (Test-Path Env:KUBECONFIG) {
$KubeConfig = Get-ChildItem -Path Env:KUBECONFIG
$KubeConfig = Get-ChildItem -Path $Env:KUBECONFIG
} elseif (Test-Path Env:Home) {
$KubeConfig = Join-Path -Path $Env:Home -ChildPath '.kube' | Join-Path -ChildPath 'config'
} else {
Expand All @@ -175,7 +175,10 @@ param(

#Region check helm install
try {
Set-HelmClientLocation
Set-HelmClientLocation `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

$HelmVersion = helm version --short --kubeconfig $KubeConfig
if ($HelmVersion.Contains("v2")) {
Write-Error "Helm version 3+ is required. Ensure that you have installed the latest version of Helm. Learn more at https://aka.ms/arc/k8s/onboarding-helm-install"
Expand All @@ -187,7 +190,10 @@ param(
#Endregion

#Region get release namespace
$ReleaseInstallNamespace = Get-ReleaseInstallNamespace
$ReleaseInstallNamespace = Get-ReleaseInstallNamespace `
-Verbose:($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) `
-Debug:($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent -eq $true)

$ReleaseNamespace = $null
try {
$ReleaseNamespace = (helm status azure-arc -o json --kubeconfig $KubeConfig --kube-context $KubeContext -n $ReleaseInstallNamespace | ConvertFrom-Json).namespace
Expand Down
Loading

0 comments on commit f159ecc

Please sign in to comment.