From c11c8fe25dba8ca472c746b3fb659a749fd22051 Mon Sep 17 00:00:00 2001 From: Abhijit Mukherjee Date: Tue, 12 Dec 2023 00:53:53 +0530 Subject: [PATCH] Update azure sdk lib (#2507) * Upgrade to new azure-go-sdk and refactor old go-autorest references Signed-off-by: mabhi * Updated test cases Signed-off-by: mabhi * Added environment struct for azure soverign cloud Signed-off-by: mabhi * Removed unused code comments Signed-off-by: mabhi * Lint error fix Signed-off-by: mabhi * Refactor the azure credential, make it explicit Signed-off-by: mabhi * Addressed review changes Signed-off-by: mabhi * Removed explicit token fetch Signed-off-by: mabhi * Added test cases for helper methods Signed-off-by: mabhi * Use deferenced disk pointer during volume list Signed-off-by: mabhi * Update snapshot deference during listing Signed-off-by: mabhi * Fix nil pointer exception during snapshot-id fetch Signed-off-by: mabhi * Reverted azure-default-credential handling Signed-off-by: Abhijit Mukherjee * Updated changes from master Signed-off-by: Abhijit Mukherjee * Addressing review comments Signed-off-by: Abhijit Mukherjee * Addressed review comments on nil pointers Signed-off-by: Abhijit Mukherjee * Addressed import grouping Signed-off-by: Abhijit Mukherjee * Added license information Signed-off-by: Abhijit Mukherjee * Address review comments Signed-off-by: Abhijit Mukherjee --------- Signed-off-by: mabhi Signed-off-by: Abhijit Mukherjee Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- go.mod | 28 +- go.sum | 38 +- pkg/blockstorage/azure/auth.go | 102 +++-- pkg/blockstorage/azure/auth_test.go | 5 +- pkg/blockstorage/azure/azuredisk.go | 450 ++++++++++++---------- pkg/blockstorage/azure/client.go | 128 +++--- pkg/blockstorage/azure/client_test.go | 50 ++- pkg/blockstorage/azure/environments.go | 98 +++++ pkg/blockstorage/helper_test.go | 60 +++ pkg/blockstorage/helpers.go | 93 +++++ pkg/kopia/command/storage/secret_utils.go | 2 +- 11 files changed, 681 insertions(+), 373 deletions(-) create mode 100644 pkg/blockstorage/azure/environments.go create mode 100644 pkg/blockstorage/helper_test.go diff --git a/go.mod b/go.mod index 096b48727e..713d84a7a3 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,10 @@ replace ( // Direct and indirect dependencies are in separate require sections require ( github.com/Azure/azure-sdk-for-go v68.0.0+incompatible - github.com/Azure/go-autorest/autorest v0.11.29 - github.com/Azure/go-autorest/autorest/adal v0.9.23 - github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 - github.com/Azure/go-autorest/autorest/to v0.4.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig v2.22.0+incompatible github.com/aws/aws-sdk-go v1.48.12 @@ -63,6 +63,7 @@ require ( sigs.k8s.io/controller-runtime v0.14.7 sigs.k8s.io/kustomize/kyaml v0.13.9 sigs.k8s.io/yaml v1.3.0 + ) require ( @@ -71,16 +72,13 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect cloud.google.com/go/storage v1.35.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 // indirect; indirect; github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -94,7 +92,6 @@ require ( github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect @@ -143,6 +140,7 @@ require ( github.com/klauspost/reedsolomon v1.11.8 // indirect github.com/kopia/htmluibuild v0.0.1-0.20231019063300-75c2a788c7d0 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -151,7 +149,6 @@ require ( github.com/minio/minio-go/v7 v7.0.65 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -164,6 +161,7 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/profile v1.7.0 // indirect github.com/pkg/sftp v1.13.6 // indirect github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 // indirect @@ -218,17 +216,19 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) +require github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 // indirect + require ( - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 // indirect + github.com/Azure/go-autorest/autorest v0.11.27 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect go.opentelemetry.io/otel/metric v1.21.0 // indirect diff --git a/go.sum b/go.sum index 247419ccf6..e48743b600 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,18 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZM github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.0.0 h1:nBy98uKOIfun5z6wx6jwWLrULcM0+cjBalBFZlEZ7CA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.0.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0 h1:Pmy0+3ox1IC3sp6musv87BFPIdQbqyPFjn7I8I0o2Js= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.2.0/go.mod h1:ThfyMjs6auYrWPnYJjI3H4H++oVPrz01pizpu8lfl3A= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+3LoSsYf9YMjkupeAnHMX8O9mmY= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk= @@ -43,19 +53,12 @@ github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= -github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= @@ -68,8 +71,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9A github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= @@ -139,8 +140,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= @@ -417,8 +416,6 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -525,7 +522,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU= @@ -600,9 +596,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= @@ -691,7 +685,6 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= @@ -703,7 +696,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/pkg/blockstorage/azure/auth.go b/pkg/blockstorage/azure/auth.go index 524330f7bb..d9e462532c 100644 --- a/pkg/blockstorage/azure/auth.go +++ b/pkg/blockstorage/azure/auth.go @@ -1,33 +1,58 @@ package azure import ( - "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/pkg/errors" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure/auth" "github.com/kanisterio/kanister/pkg/blockstorage" - "github.com/pkg/errors" ) +const ActiveDirectory = "activeDirectory" + // currently avaialble types: https://docs.microsoft.com/en-us/azure/developer/go/azure-sdk-authorization // to be available with azidentity: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-credential-types // determine if the combination of creds are client secret creds func isClientCredsAvailable(config map[string]string) bool { - return (config[blockstorage.AzureTenantID] != "" && + return config[blockstorage.AzureTenantID] != "" && config[blockstorage.AzureClientID] != "" && - config[blockstorage.AzureClientSecret] != "") + config[blockstorage.AzureClientSecret] != "" } // determine if the combination of creds are MSI creds func isMSICredsAvailable(config map[string]string) bool { _, clientIDok := config[blockstorage.AzureClientID] - return (clientIDok && config[blockstorage.AzureTenantID] == "" && - config[blockstorage.AzureClientSecret] == "") + return clientIDok && config[blockstorage.AzureTenantID] == "" && + config[blockstorage.AzureClientSecret] == "" +} + +type ClientCredentialsConfig struct { + ClientID string + ClientSecret string + TenantID string + AuxTenants []string + AADEndpoint string + Resource string +} + +// Defaults to Public Cloud and Resource Manager Endpoint. +func NewClientCredentialsConfig(clientID string, clientSecret string, tenantID string) ClientCredentialsConfig { + return ClientCredentialsConfig{ + ClientID: clientID, + ClientSecret: clientSecret, + TenantID: tenantID, + Resource: cloud.AzurePublic.Services[cloud.ResourceManager].Endpoint, + //Todo: find a replacement for the AADEndpoint in the new azure sdk + AADEndpoint: cloud.AzurePublic.Services[ActiveDirectory].Endpoint, + // azure.PublicCloud.ActiveDirectoryEndpoint, + } } // Public interface to authenticate with different Azure credentials type type AzureAuthenticator interface { Authenticate(creds map[string]string) error + GetAuthorizer() azcore.TokenCredential } func NewAzureAuthenticator(config map[string]string) (AzureAuthenticator, error) { @@ -42,69 +67,68 @@ func NewAzureAuthenticator(config map[string]string) (AzureAuthenticator, error) } // authenticate with MSI creds -type MsiAuthenticator struct{} +type MsiAuthenticator struct { + azcore.TokenCredential +} -func (m *MsiAuthenticator) Authenticate(creds map[string]string) error { +func (m *MsiAuthenticator) GetAuthorizer() azcore.TokenCredential { + return m.TokenCredential +} +func (m *MsiAuthenticator) Authenticate(config map[string]string) error { // check if MSI endpoint is available - if !adal.MSIAvailable(context.Background(), nil) { - return errors.New("MSI endpoint is not supported") - } - // create a service principal token - msiConfig := auth.NewMSIConfig() - if clientID, ok := creds[blockstorage.AzureClientID]; ok && clientID != "" { - msiConfig.ClientID = clientID - } - spt, err := msiConfig.ServicePrincipalToken() - if err != nil { - return errors.Wrap(err, "Failed to create a service principal token") + clientID, ok := config[blockstorage.AzureClientID] + if !ok || clientID == "" { + return errors.New("Failed to fetch azure clientID") } - // network call to check for token - err = spt.Refresh() + azClientID := azidentity.ClientID(clientID) + opts := azidentity.ManagedIdentityCredentialOptions{ID: azClientID} + cred, err := azidentity.NewManagedIdentityCredential(&opts) if err != nil { - return errors.Wrap(err, "Failed to refresh token") + return errors.Wrap(err, "Failed to create an Azure Managed Identity credential") } - // creds passed authentication + m.TokenCredential = cred + // config passed authentication return nil } // authenticate with client secret creds -type ClientSecretAuthenticator struct{} +type ClientSecretAuthenticator struct { + azcore.TokenCredential +} +func (c *ClientSecretAuthenticator) GetAuthorizer() azcore.TokenCredential { + return c.TokenCredential +} func (c *ClientSecretAuthenticator) Authenticate(creds map[string]string) error { credConfig, err := getCredConfigForAuth(creds) if err != nil { return errors.Wrap(err, "Failed to get Client Secret config") } - // create a service principal token - spt, err := credConfig.ServicePrincipalToken() - if err != nil { - return errors.Wrap(err, "Failed to create a service principal token") - } - // network call to check for token - err = spt.Refresh() + cred, err := azidentity.NewClientSecretCredential(credConfig.TenantID, credConfig.ClientID, credConfig.ClientSecret, nil) if err != nil { - return errors.Wrap(err, "Failed to refresh token") + return errors.Wrap(err, "Failed to create an Azure Client Secret credential") } + c.TokenCredential = cred // creds passed authentication return nil } -func getCredConfigForAuth(config map[string]string) (auth.ClientCredentialsConfig, error) { +func getCredConfigForAuth(config map[string]string) (ClientCredentialsConfig, error) { tenantID, ok := config[blockstorage.AzureTenantID] if !ok { - return auth.ClientCredentialsConfig{}, errors.New("Cannot get tenantID from config") + return ClientCredentialsConfig{}, errors.New("Cannot get tenantID from config") } clientID, ok := config[blockstorage.AzureClientID] if !ok { - return auth.ClientCredentialsConfig{}, errors.New("Cannot get clientID from config") + return ClientCredentialsConfig{}, errors.New("Cannot get clientID from config") } clientSecret, ok := config[blockstorage.AzureClientSecret] if !ok { - return auth.ClientCredentialsConfig{}, errors.New("Cannot get clientSecret from config") + return ClientCredentialsConfig{}, errors.New("Cannot get clientSecret from config") } - credConfig := auth.NewClientCredentialsConfig(clientID, clientSecret, tenantID) + credConfig := NewClientCredentialsConfig(clientID, clientSecret, tenantID) return credConfig, nil } diff --git a/pkg/blockstorage/azure/auth_test.go b/pkg/blockstorage/azure/auth_test.go index 20573892dc..e156e7c303 100644 --- a/pkg/blockstorage/azure/auth_test.go +++ b/pkg/blockstorage/azure/auth_test.go @@ -15,8 +15,9 @@ package azure import ( - "github.com/kanisterio/kanister/pkg/blockstorage" . "gopkg.in/check.v1" + + "github.com/kanisterio/kanister/pkg/blockstorage" ) type AuthSuite struct{} @@ -72,7 +73,7 @@ func (s *AuthSuite) TestIsMSICredsAvailable(c *C) { c.Assert(isMSICredsAvailable(config), Equals, false) } -func (s *AuthSuite) TestNewAzureAutheticator(c *C) { +func (s *AuthSuite) TestNewAzureAuthenticator(c *C) { // successful with client secret creds config := map[string]string{ blockstorage.AzureTenantID: "some-tenant-id", diff --git a/pkg/blockstorage/azure/azuredisk.go b/pkg/blockstorage/azure/azuredisk.go index c74a52cbbd..8361757e19 100644 --- a/pkg/blockstorage/azure/azuredisk.go +++ b/pkg/blockstorage/azure/azuredisk.go @@ -2,6 +2,7 @@ // Related Ticket- https://github.com/kanisterio/kanister/issues/1684 // //nolint:staticcheck + package azure import ( @@ -11,12 +12,11 @@ import ( "strings" "time" - "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/skus" - "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions" - azcompute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" + azto "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions" "github.com/Azure/azure-sdk-for-go/storage" - azto "github.com/Azure/go-autorest/autorest/to" - uuid "github.com/gofrs/uuid" + "github.com/gofrs/uuid" "github.com/pkg/errors" "github.com/kanisterio/kanister/pkg/blockstorage" @@ -39,6 +39,8 @@ const ( copyBlobName = "copy-blob-%s.vhd" ) +type LocationZoneMap map[string]struct{} + // AdStorage describes the azure storage client type AdStorage struct { azCli *Client @@ -63,11 +65,11 @@ func (s *AdStorage) VolumeGet(ctx context.Context, id string, zone string) (*blo return nil, errors.Wrapf(err, "Failed to get info for volume with ID %s", id) } - disk, err := s.azCli.DisksClient.Get(ctx, rg, name) + diskResponse, err := s.azCli.DisksClient.Get(ctx, rg, name, nil) if err != nil { return nil, errors.Wrapf(err, "Failed to get volume, volumeID: %s", id) } - return s.VolumeParse(ctx, disk) + return s.VolumeParse(ctx, diskResponse.Disk) } func (s *AdStorage) VolumeCreate(ctx context.Context, volume blockstorage.Volume) (*blockstorage.Volume, error) { @@ -77,43 +79,40 @@ func (s *AdStorage) VolumeCreate(ctx context.Context, volume blockstorage.Volume return nil, errors.Wrap(err, "Failed to create UUID") } diskName := fmt.Sprintf(volumeNameFmt, diskId.String()) - diskProperties := &azcompute.DiskProperties{ - DiskSizeGB: azto.Int32Ptr(int32(blockstorage.SizeInGi(volume.SizeInBytes))), - CreationData: &azcompute.CreationData{ - CreateOption: azcompute.DiskCreateOption(azcompute.DiskCreateOptionTypesEmpty), + + diskProperties := &armcompute.DiskProperties{ + CreationData: &armcompute.CreationData{ + CreateOption: azto.Ptr(armcompute.DiskCreateOptionEmpty), }, + DiskSizeGB: blockstorage.Int32Ptr(int32(blockstorage.SizeInGi(volume.SizeInBytes))), } region, id, err := getLocationInfo(volume.Az) if err != nil { return nil, errors.Wrapf(err, "Could not get region from zone %s", volume.Az) } // TODO(ilya): figure out how to create SKUed disks - createDisk := azcompute.Disk{ - Name: azto.StringPtr(diskName), - Tags: *azto.StringMapPtr(tags), - Location: azto.StringPtr(region), - DiskProperties: diskProperties, + createdDisk := armcompute.Disk{ + Name: blockstorage.StringPtr(diskName), + Tags: *blockstorage.StringMapPtr(tags), + Location: blockstorage.StringPtr(region), + Properties: diskProperties, + SKU: &armcompute.DiskSKU{ + Name: azto.Ptr(armcompute.DiskStorageAccountTypesStandardLRS), + }, } if id != "" { - createDisk.Zones = azto.StringSlicePtr([]string{id}) + createdDisk.Zones = blockstorage.SliceStringPtr([]string{id}) } - result, err := s.azCli.DisksClient.CreateOrUpdate(ctx, s.azCli.ResourceGroup, diskName, createDisk) - if err != nil { - return nil, err - } - err = result.WaitForCompletionRef(ctx, s.azCli.DisksClient.Client) + + pollerResp, err := s.azCli.DisksClient.BeginCreateOrUpdate(ctx, s.azCli.ResourceGroup, diskName, createdDisk, nil) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Could not create volume %s", diskName) } - disk, err := result.Result(*s.azCli.DisksClient) + resp, err := pollerResp.PollUntilDone(ctx, nil) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Volume create %s polling error", diskName) } - - // Even though the 'CreateOrUpdate' call above returns a 'Disk' model, this is incomplete and - // requires a GET to populate correctly. - // See https://github.com/Azure/azure-sdk-for-go/issues/326 for the explanation why - return s.VolumeGet(ctx, azto.String(disk.ID), volume.Az) + return s.VolumeParse(ctx, resp.Disk) } func (s *AdStorage) VolumeDelete(ctx context.Context, volume *blockstorage.Volume) error { @@ -121,11 +120,11 @@ func (s *AdStorage) VolumeDelete(ctx context.Context, volume *blockstorage.Volum if err != nil { return errors.Wrapf(err, "Error in deleting Volume with ID %s", volume.ID) } - result, err := s.azCli.DisksClient.Delete(ctx, rg, name) + poller, err := s.azCli.DisksClient.BeginDelete(ctx, rg, name, nil) if err != nil { return errors.Wrapf(err, "Error in deleting Volume with ID %s", volume.ID) } - err = result.WaitForCompletionRef(ctx, s.azCli.DisksClient.Client) + _, err = poller.PollUntilDone(ctx, nil) return errors.Wrapf(err, "Error in deleting Volume with ID %s", volume.ID) } @@ -154,38 +153,27 @@ func (s *AdStorage) SnapshotCopyWithArgs(ctx context.Context, from blockstorage. if err != nil { return nil, errors.Wrapf(err, "SnapshotsClient.Copy: Failure in parsing snapshot ID %s", from.ID) } - _, err = s.azCli.SnapshotsClient.Get(ctx, rg, name) + _, err = s.azCli.SnapshotsClient.Get(ctx, rg, name, nil) if err != nil { return nil, errors.Wrapf(err, "SnapshotsClient.Copy: Failed to get snapshot with ID %s", from.ID) } duration := int32(3600) - gad := azcompute.GrantAccessData{ - Access: azcompute.Read, + gad := armcompute.GrantAccessData{ + Access: azto.Ptr(armcompute.AccessLevelRead), DurationInSeconds: &duration, } - snapshotsGrantAccessFuture, err := s.azCli.SnapshotsClient.GrantAccess(ctx, rg, name, gad) + snapshotsGrantAccessPoller, err := s.azCli.SnapshotsClient.BeginGrantAccess(ctx, rg, name, gad, nil) if err != nil { return nil, errors.Wrapf(err, "Failed to grant read access to snapshot: %s", from.ID) } defer s.revokeAccess(ctx, rg, name, from.ID) - - err = poll.Wait(ctx, func(ctx context.Context) (bool, error) { - _, err := snapshotsGrantAccessFuture.Result(*s.azCli.SnapshotsClient) - if err != nil { - if strings.Contains(err.Error(), "asynchronous operation has not completed") { - return false, nil - } - return false, err - } - return true, nil - }) + snapshotGrantRes, err := snapshotsGrantAccessPoller.PollUntilDone(ctx, nil) if err != nil { - return nil, errors.Wrap(err, "SnapshotsClient.Copy failure to grant snapshot access") + return nil, errors.Wrap(err, "SnapshotsClient.Copy failure to grant snapshot access. Snapshot grant access poller failed to pull the result") } - accessURI, err := snapshotsGrantAccessFuture.Result(*s.azCli.SnapshotsClient) if err != nil { return nil, errors.Wrap(err, "SnapshotsClient.Copy failure to grant snapshot access") } @@ -203,7 +191,7 @@ func (s *AdStorage) SnapshotCopyWithArgs(ctx context.Context, from blockstorage. if err != nil { return nil, err } - err = blob.Copy(*accessURI.AccessSAS, copyOptions) + err = blob.Copy(*snapshotGrantRes.AccessSAS, copyOptions) if err != nil { return nil, errors.Wrapf(err, "Failed to copy disk to blob") } @@ -218,20 +206,15 @@ func (s *AdStorage) SnapshotCopyWithArgs(ctx context.Context, from blockstorage. if val, ok := args[blockstorage.AzureMigrateResourceGroup]; ok && val != "" { migrateResourceGroup = val } - result, err := s.azCli.SnapshotsClient.CreateOrUpdate(ctx, migrateResourceGroup, snapName, createSnap) + createSnapshotPoller, err := s.azCli.SnapshotsClient.BeginCreateOrUpdate(ctx, migrateResourceGroup, snapName, createSnap, nil) if err != nil { return nil, errors.Wrapf(err, "Failed to copy snapshot from source snapshot %v", from) } - err = result.WaitForCompletionRef(ctx, s.azCli.SnapshotsClient.Client) + createSnapRes, err := createSnapshotPoller.PollUntilDone(ctx, nil) if err != nil { - return nil, errors.Wrapf(err, "Failed to copy snapshot from source snapshot %v", from) + return nil, errors.Wrap(err, "Poller failed to retrieve snapshot") } - rs, err := result.Result(*s.azCli.SnapshotsClient) - if err != nil { - return nil, errors.Wrapf(err, "Error in getting result of Snapshot copy operation, snaphotName %s", snapName) - } - - snap, err := s.SnapshotGet(ctx, azto.String(rs.ID)) + snap, err := s.SnapshotGet(ctx, blockstorage.StringFromPtr(createSnapRes.ID)) if err != nil { return nil, errors.Wrapf(err, "Failed to Get Snapshot after create, snaphotName %s", snapName) } @@ -259,7 +242,7 @@ func getSnapshotObject( to blockstorage.Snapshot, snapName, storageAccountID string, -) azcompute.Snapshot { +) armcompute.Snapshot { blobURI := blob.GetURL() var tags = make(map[string]string) @@ -270,15 +253,15 @@ func getSnapshotObject( } tags = blockstorage.SanitizeTags(ktags.GetTags(tags)) - createSnap := azcompute.Snapshot{ - Name: azto.StringPtr(snapName), - Location: azto.StringPtr(to.Region), - Tags: *azto.StringMapPtr(tags), - SnapshotProperties: &azcompute.SnapshotProperties{ - CreationData: &azcompute.CreationData{ - CreateOption: azcompute.Import, - StorageAccountID: azto.StringPtr(storageAccountID), - SourceURI: azto.StringPtr(blobURI), + createSnap := armcompute.Snapshot{ + Name: blockstorage.StringPtr(snapName), + Location: blockstorage.StringPtr(to.Region), + Tags: *blockstorage.StringMapPtr(tags), + Properties: &armcompute.SnapshotProperties{ + CreationData: &armcompute.CreationData{ + CreateOption: azto.Ptr(armcompute.DiskCreateOptionImport), + StorageAccountID: blockstorage.StringPtr(storageAccountID), + SourceURI: blockstorage.StringPtr(blobURI), }, }, } @@ -290,7 +273,15 @@ func isMigrateStorageAccountorKey(migrateStorageAccount, migrateStorageKey strin } func (s *AdStorage) revokeAccess(ctx context.Context, rg, name, id string) { - _, err := s.azCli.SnapshotsClient.RevokeAccess(ctx, rg, name) + poller, err := s.azCli.SnapshotsClient.BeginRevokeAccess(ctx, rg, name, nil) + if err != nil { + log.Print("Failed to finish the revoke request", field.M{"error": err.Error()}) + } + _, err = poller.PollUntilDone(ctx, nil) + if err != nil { + log.Print("failed to pull the result", field.M{"error": err.Error()}) + } + if err != nil { log.Print("Failed to revoke access from snapshot", field.M{"snapshot": id}) } @@ -314,42 +305,38 @@ func (s *AdStorage) SnapshotCreate(ctx context.Context, volume blockstorage.Volu if err != nil { return nil, errors.Wrapf(err, "Could not get region from zone %s", volume.Az) } - createSnap := azcompute.Snapshot{ - Name: azto.StringPtr(snapName), - Location: azto.StringPtr(region), - Tags: *azto.StringMapPtr(tags), - SnapshotProperties: &azcompute.SnapshotProperties{ - CreationData: &azcompute.CreationData{ - CreateOption: azcompute.Copy, - SourceResourceID: azto.StringPtr(volume.ID), + createSnap := armcompute.Snapshot{ + Name: blockstorage.StringPtr(snapName), + Location: blockstorage.StringPtr(region), + Tags: *blockstorage.StringMapPtr(tags), + Properties: &armcompute.SnapshotProperties{ + CreationData: &armcompute.CreationData{ + CreateOption: azto.Ptr(armcompute.DiskCreateOptionCopy), + SourceResourceID: blockstorage.StringPtr(volume.ID), }, }, } - result, err := s.azCli.SnapshotsClient.CreateOrUpdate(ctx, s.azCli.ResourceGroup, snapName, createSnap) + pollerResp, err := s.azCli.SnapshotsClient.BeginCreateOrUpdate(ctx, s.azCli.ResourceGroup, snapName, createSnap, nil) if err != nil { return nil, errors.Wrapf(err, "Failed to create snapshot for volume %v", volume) } - err = result.WaitForCompletionRef(ctx, s.azCli.SnapshotsClient.Client) + resp, err := pollerResp.PollUntilDone(ctx, nil) if err != nil { - return nil, errors.Wrapf(err, "Failed to create snapshot for volume %v", volume) + return nil, errors.Wrapf(err, "Failed to Get Snapshot after create, snaphotName %s", snapName) } - rs, err := result.Result(*s.azCli.SnapshotsClient) + blockSnapshot, err := s.snapshotParse(ctx, resp.Snapshot) if err != nil { - return nil, errors.Wrapf(err, "Error in getting result of Snapshot create operation, snaphotName %s", snapName) + return nil, errors.Wrapf(err, "Failed to Parse Snapshot, snaphotName %s", snapName) } - snap, err := s.SnapshotGet(ctx, azto.String(rs.ID)) - if err != nil { - return nil, errors.Wrapf(err, "Failed to Get Snapshot after create, snaphotName %s", snapName) - } - snap.Volume = &volume - return snap, nil + blockSnapshot.Volume = &volume + return blockSnapshot, nil } func (s *AdStorage) SnapshotCreateWaitForCompletion(ctx context.Context, snap *blockstorage.Snapshot) error { err := poll.Wait(ctx, func(ctx context.Context) (bool, error) { snapshot, err := s.SnapshotGet(ctx, snap.ID) - if err == nil && snapshot.ProvisioningState == string(azcompute.ProvisioningStateSucceeded) { + if err == nil && snapshot.ProvisioningState == string(armcompute.GalleryProvisioningStateSucceeded) { return true, nil } @@ -389,12 +376,11 @@ func (s *AdStorage) SnapshotDelete(ctx context.Context, snapshot *blockstorage.S if err != nil { return errors.Wrapf(err, "SnapshotClient.Delete: Failure in parsing snapshot ID %s", snapshot.ID) } - result, err := s.azCli.SnapshotsClient.Delete(ctx, rg, name) + poller, err := s.azCli.SnapshotsClient.BeginDelete(ctx, rg, name, nil) if err != nil { return errors.Wrapf(err, "SnapshotClient.Delete: Failed to delete snapshot with ID %s", snapshot.ID) } - err = result.WaitForCompletionRef(ctx, s.azCli.SnapshotsClient.Client) - + _, err = poller.PollUntilDone(ctx, nil) return errors.Wrapf(err, "SnapshotClient.Delete: Error while waiting for snapshot with ID %s to get deleted", snapshot.ID) } @@ -403,96 +389,149 @@ func (s *AdStorage) SnapshotGet(ctx context.Context, id string) (*blockstorage.S if err != nil { return nil, errors.Wrapf(err, "SnapshotsClient.Get: Failure in parsing snapshot ID %s", id) } - snap, err := s.azCli.SnapshotsClient.Get(ctx, rg, name) + snapRes, err := s.azCli.SnapshotsClient.Get(ctx, rg, name, nil) if err != nil { return nil, errors.Wrapf(err, "SnapshotsClient.Get: Failed to get snapshot with ID %s", id) } - return s.snapshotParse(ctx, snap), nil + return s.snapshotParse(ctx, snapRes.Snapshot) } func (s *AdStorage) VolumeParse(ctx context.Context, volume interface{}) (*blockstorage.Volume, error) { - vol, ok := volume.(azcompute.Disk) + vol, ok := volume.(armcompute.Disk) if !ok { - return nil, errors.New(fmt.Sprintf("Volume is not of type *azcompute.Disk, volume: %v", volume)) + return nil, errors.New(fmt.Sprintf("Volume is not of type *armcompute.Disk, volume: %v", volume)) } encrypted := false - if vol.DiskProperties.EncryptionSettingsCollection != nil && - vol.DiskProperties.EncryptionSettingsCollection.Enabled != nil { - encrypted = *vol.DiskProperties.EncryptionSettingsCollection.Enabled + if vol.Properties.EncryptionSettingsCollection != nil && + vol.Properties.EncryptionSettingsCollection.Enabled != nil { + encrypted = *vol.Properties.EncryptionSettingsCollection.Enabled } tags := map[string]string{"": ""} if vol.Tags != nil { - tags = azto.StringMap(vol.Tags) + tags = blockstorage.StringMap(vol.Tags) } - az := azto.String(vol.Location) - if z := azto.StringSlice(vol.Zones); len(z) > 0 { - az = az + "-" + z[0] + az := blockstorage.StringFromPtr(vol.Location) + if z := vol.Zones; len(z) > 0 { + az = az + "-" + *(z[0]) + } + volumeType := "" + if vol.SKU != nil && + vol.SKU.Name != nil { + volumeType = string(*vol.SKU.Name) + } else { + return nil, errors.New("Volume type is not available") + } + + volId := "" + if vol.ID != nil { + volId = blockstorage.StringFromPtr(vol.ID) + } else { + return nil, errors.New("Volume Id is not available") + } + diskSize := int64(0) + if vol.Properties != nil && + vol.Properties.DiskSizeBytes != nil { + diskSize = blockstorage.Int64(vol.Properties.DiskSizeBytes) + } + + var creationTime = time.Now() + if vol.Properties != nil && vol.Properties.TimeCreated != nil { + creationTime = *vol.Properties.TimeCreated + } + + var managedBy = "N.A." + if vol.ManagedBy != nil { + managedBy = blockstorage.StringFromPtr(vol.ManagedBy) } return &blockstorage.Volume{ Type: s.Type(), - ID: azto.String(vol.ID), + ID: volId, Encrypted: encrypted, - SizeInBytes: azto.Int64(vol.DiskSizeBytes), + SizeInBytes: diskSize, Az: az, Tags: blockstorage.MapToKeyValue(tags), - VolumeType: string(vol.Sku.Name), - CreationTime: blockstorage.TimeStamp(vol.DiskProperties.TimeCreated.ToTime()), - Attributes: map[string]string{"Users": azto.String(vol.ManagedBy)}, + VolumeType: volumeType, + CreationTime: blockstorage.TimeStamp(creationTime), + Attributes: map[string]string{"Users": managedBy}, }, nil } func (s *AdStorage) SnapshotParse(ctx context.Context, snapshot interface{}) (*blockstorage.Snapshot, error) { - if snap, ok := snapshot.(azcompute.Snapshot); ok { - return s.snapshotParse(ctx, snap), nil + if snap, ok := snapshot.(armcompute.Snapshot); ok { + return s.snapshotParse(ctx, snap) } - return nil, errors.New(fmt.Sprintf("Snapshot is not of type *azcompute.Snapshot, snapshot: %v", snapshot)) + return nil, errors.New(fmt.Sprintf("Snapshot is not of type *armcompute.Snapshot, snapshot: %v", snapshot)) } -func (s *AdStorage) snapshotParse(ctx context.Context, snap azcompute.Snapshot) *blockstorage.Snapshot { +func (s *AdStorage) snapshotParse(ctx context.Context, snap armcompute.Snapshot) (*blockstorage.Snapshot, error) { + snapId := "" + if snap.ID != nil { + snapId = *snap.ID + } else { + return nil, errors.New("Snapshot ID is missing") + } vol := &blockstorage.Volume{ Type: s.Type(), - ID: azto.String(snap.SnapshotProperties.CreationData.SourceResourceID), + ID: snapId, + } + + snapCreationTime := time.Now() + if snap.Properties != nil && snap.Properties.TimeCreated != nil { + snapCreationTime = *snap.Properties.TimeCreated } - snapCreationTime := *snap.TimeCreated encrypted := false - if snap.SnapshotProperties.EncryptionSettingsCollection != nil && - snap.SnapshotProperties.EncryptionSettingsCollection.Enabled != nil { - encrypted = *snap.SnapshotProperties.EncryptionSettingsCollection.Enabled + if snap.Properties.EncryptionSettingsCollection != nil && + snap.Properties.EncryptionSettingsCollection.Enabled != nil { + encrypted = *snap.Properties.EncryptionSettingsCollection.Enabled } tags := map[string]string{} if snap.Tags != nil { - tags = azto.StringMap(snap.Tags) + tags = blockstorage.StringMap(snap.Tags) + } + + diskSize := azto.Ptr(int64(0)) + if snap.Properties != nil && + snap.Properties.DiskSizeBytes != nil { + diskSize = snap.Properties.DiskSizeBytes + } + + region := "" + if snap.Location != nil { + region = *snap.Location } return &blockstorage.Snapshot{ Encrypted: encrypted, - ID: azto.String(snap.ID), - Region: azto.String(snap.Location), - SizeInBytes: azto.Int64(snap.SnapshotProperties.DiskSizeBytes), + ID: snapId, + Region: region, + SizeInBytes: blockstorage.Int64(diskSize), Tags: blockstorage.MapToKeyValue(tags), Type: s.Type(), Volume: vol, - CreationTime: blockstorage.TimeStamp(snapCreationTime.ToTime()), - ProvisioningState: *snap.ProvisioningState, - } + CreationTime: blockstorage.TimeStamp(snapCreationTime), + ProvisioningState: *snap.Properties.ProvisioningState, + }, nil } func (s *AdStorage) VolumesList(ctx context.Context, tags map[string]string, zone string) ([]*blockstorage.Volume, error) { var vols []*blockstorage.Volume // (ilya): It looks like azure doesn't support search by tags // List does listing per Subscription - for diskList, err := s.azCli.DisksClient.ListComplete(ctx); diskList.NotDone(); err = diskList.Next() { + pager := s.azCli.DisksClient.NewListPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) if err != nil { return nil, errors.Wrap(err, "DisksClient.List in VolumesList") } - disk := diskList.Value() - vol, err := s.VolumeParse(ctx, disk) - if err != nil { - return nil, errors.Wrap(err, "DisksClient.List in VolumesList, failure in parsing Volume") + for _, disk := range page.Value { + vol, err := s.VolumeParse(ctx, *disk) + if err != nil { + return nil, errors.Wrap(err, "DisksClient.List in VolumesList, failure in parsing Volume") + } + vols = append(vols, vol) } - vols = append(vols, vol) } return vols, nil } @@ -501,17 +540,20 @@ func (s *AdStorage) SnapshotsList(ctx context.Context, tags map[string]string) ( var snaps []*blockstorage.Snapshot // (ilya): It looks like azure doesn't support search by tags // List does listing per Subscription - for snapList, err := s.azCli.SnapshotsClient.ListComplete(ctx); snapList.NotDone(); err = snapList.Next() { + pager := s.azCli.SnapshotsClient.NewListPager(nil) + for pager.More() { + page, err := pager.NextPage(ctx) if err != nil { return nil, errors.Wrap(err, "SnapshotsClient.List in SnapshotsList") } - snap := snapList.Value() - k10Snap, err := s.SnapshotParse(ctx, snap) - if err != nil { - log.WithError(err).Print("Incorrect Snaphost type", field.M{"SnapshotID": snap.ID}) - continue + for _, snap := range page.Value { + k10Snap, err := s.SnapshotParse(ctx, *snap) + if err != nil { + log.WithError(err).Print("Incorrect Snaphost type", field.M{"SnapshotID": snap.ID}) + continue + } + snaps = append(snaps, k10Snap) } - snaps = append(snaps, k10Snap) } snaps = blockstorage.FilterSnapshotsWithTags(snaps, blockstorage.SanitizeTags(tags)) return snaps, nil @@ -537,39 +579,36 @@ func (s *AdStorage) VolumeCreateFromSnapshot(ctx context.Context, snapshot block } diskName := fmt.Sprintf(volumeNameFmt, diskId.String()) tags = blockstorage.SanitizeTags(tags) - createDisk := azcompute.Disk{ - Name: azto.StringPtr(diskName), - Tags: *azto.StringMapPtr(tags), - Location: azto.StringPtr(region), - DiskProperties: &azcompute.DiskProperties{ - CreationData: &azcompute.CreationData{ - CreateOption: azcompute.Copy, - SourceResourceID: azto.StringPtr(snapshot.ID), + createDisk := armcompute.Disk{ + Name: blockstorage.StringPtr(diskName), + Tags: *blockstorage.StringMapPtr(tags), + Location: blockstorage.StringPtr(region), + Properties: &armcompute.DiskProperties{ + CreationData: &armcompute.CreationData{ + CreateOption: azto.Ptr(armcompute.DiskCreateOptionCopy), + SourceResourceID: blockstorage.StringPtr(snapshot.ID), }, }, } if id != "" { - createDisk.Zones = azto.StringSlicePtr([]string{id}) + createDisk.Zones = blockstorage.SliceStringPtr([]string{id}) } - for _, saType := range azcompute.PossibleDiskStorageAccountTypesValues() { + for _, saType := range armcompute.PossibleDiskStorageAccountTypesValues() { if string(saType) == snapshot.Volume.VolumeType { - createDisk.Sku = &azcompute.DiskSku{ - Name: saType, + createDisk.SKU = &armcompute.DiskSKU{ + Name: azto.Ptr(saType), } } } - result, err := s.azCli.DisksClient.CreateOrUpdate(ctx, s.azCli.ResourceGroup, diskName, createDisk) + poller, err := s.azCli.DisksClient.BeginCreateOrUpdate(ctx, s.azCli.ResourceGroup, diskName, createDisk, nil) if err != nil { return nil, errors.Wrapf(err, "DiskCLient.CreateOrUpdate in VolumeCreateFromSnapshot, diskName: %s, snapshotID: %s", diskName, snapshot.ID) } - if err = result.WaitForCompletionRef(ctx, s.azCli.DisksClient.Client); err != nil { - return nil, errors.Wrapf(err, "DiskCLient.CreateOrUpdate in VolumeCreateFromSnapshot, diskName: %s, snapshotID: %s", diskName, snapshot.ID) - } - disk, err := result.Result(*s.azCli.DisksClient) + resp, err := poller.PollUntilDone(ctx, nil) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "DiskCLient.CreateOrUpdate in VolumeCreateFromSnapshot, diskName: %s, snapshotID: %s", diskName, snapshot.ID) } - return s.VolumeGet(ctx, azto.String(disk.ID), snapshot.Volume.Az) + return s.VolumeParse(ctx, resp.Disk) } func (s *AdStorage) getRegionAndZoneID(ctx context.Context, sourceRegion, volAz string) (string, string, error) { @@ -622,19 +661,19 @@ func (s *AdStorage) SetTags(ctx context.Context, resource interface{}, tags map[ if err != nil { return err } - snap, err := s.azCli.SnapshotsClient.Get(ctx, rg, name) + snap, err := s.azCli.SnapshotsClient.Get(ctx, rg, name, nil) if err != nil { return errors.Wrapf(err, "SnapshotsClient.Get in SetTags, snapshotID: %s", res.ID) } - tags = ktags.AddMissingTags(azto.StringMap(snap.Tags), ktags.GetTags(tags)) - snapProperties := azcompute.SnapshotUpdate{ - Tags: *azto.StringMapPtr(blockstorage.SanitizeTags(tags)), + tags = ktags.AddMissingTags(blockstorage.StringMap(snap.Tags), ktags.GetTags(tags)) + snapProperties := armcompute.SnapshotUpdate{ + Tags: *blockstorage.StringMapPtr(blockstorage.SanitizeTags(tags)), } - result, err := s.azCli.SnapshotsClient.Update(ctx, rg, name, snapProperties) + poller, err := s.azCli.SnapshotsClient.BeginUpdate(ctx, rg, name, snapProperties, nil) if err != nil { return errors.Wrapf(err, "SnapshotsClient.Update in SetTags, snapshotID: %s", name) } - err = result.WaitForCompletionRef(ctx, s.azCli.SnapshotsClient.Client) + _, err = poller.PollUntilDone(ctx, nil) return errors.Wrapf(err, "SnapshotsClient.Update in SetTags, snapshotID: %s", name) } case *blockstorage.Volume: @@ -643,20 +682,20 @@ func (s *AdStorage) SetTags(ctx context.Context, resource interface{}, tags map[ if err != nil { return err } - vol, err := s.azCli.DisksClient.Get(ctx, rg, volID) + vol, err := s.azCli.DisksClient.Get(ctx, rg, volID, nil) if err != nil { return errors.Wrapf(err, "DiskClient.Get in SetTags, volumeID: %s", volID) } - tags = ktags.AddMissingTags(azto.StringMap(vol.Tags), ktags.GetTags(tags)) + tags = ktags.AddMissingTags(blockstorage.StringMap(vol.Tags), ktags.GetTags(tags)) - diskProperties := azcompute.DiskUpdate{ - Tags: *azto.StringMapPtr(blockstorage.SanitizeTags(tags)), + diskProperties := armcompute.DiskUpdate{ + Tags: *blockstorage.StringMapPtr(blockstorage.SanitizeTags(tags)), } - result, err := s.azCli.DisksClient.Update(ctx, rg, volID, diskProperties) + poller, err := s.azCli.DisksClient.BeginUpdate(ctx, rg, volID, diskProperties, nil) if err != nil { return errors.Wrapf(err, "DiskClient.Update in SetTags, volumeID: %s", volID) } - err = result.WaitForCompletionRef(ctx, s.azCli.DisksClient.Client) + _, err = poller.PollUntilDone(ctx, nil) return errors.Wrapf(err, "DiskClient.Update in SetTags, volumeID: %s", volID) } default: @@ -706,39 +745,38 @@ func (s *AdStorage) SnapshotRestoreTargets(ctx context.Context, snapshot *blocks // dynamicRegionMapAzure derives a mapping from Regions to zones for Azure. Depends on subscriptionID func (s *AdStorage) dynamicRegionMapAzure(ctx context.Context) (map[string][]string, error) { - subscriptionsCLient := subscriptions.NewClientWithBaseURI(s.azCli.BaseURI) - subscriptionsCLient.Authorizer = s.azCli.Authorizer - llResp, err := subscriptionsCLient.ListLocations(ctx, s.azCli.SubscriptionID, nil) - if err != nil { - return nil, err - } - regionMap := make(map[string]map[string]struct{}) - for _, location := range *llResp.Value { - regionMap[*location.Name] = make(map[string]struct{}) - } + subscriptionsClient := s.azCli.SubscriptionsClient + regionMap := make(map[string]LocationZoneMap) - skuClient := skus.NewResourceSkusClientWithBaseURI(s.azCli.BaseURI, s.azCli.SubscriptionID) - skuClient.Authorizer = s.azCli.Authorizer - skuResults, err := skuClient.ListComplete(ctx) - if err != nil { - return nil, err - } - for skuResults.Value().Name != nil { - if skuResults.Value().ResourceType != nil && *skuResults.Value().ResourceType == "disks" { - for _, location := range *skuResults.Value().LocationInfo { - if val, ok := regionMap[*location.Location]; ok { - for _, zone := range *location.Zones { - val[zone] = struct{}{} - } - regionMap[*location.Location] = val - } - } + locationsPager := subscriptionsClient.NewListLocationsPager(s.azCli.SubscriptionID, &armsubscriptions.ClientListLocationsOptions{IncludeExtendedLocations: nil}) + for locationsPager.More() { + page, err := locationsPager.NextPage(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to advance page") } - if err = skuResults.NextWithContext(ctx); err != nil { - return nil, err + for _, location := range page.Value { + if location != nil && location.Name != nil { + regionMap[*location.Name] = make(LocationZoneMap) + } else { + continue + } } } + skusClient := s.azCli.SKUsClient + skusPager := skusClient.NewListPager(&armcompute.ResourceSKUsClientListOptions{Filter: nil, + IncludeExtendedLocations: nil}) + for skusPager.More() { + skuResults, err := skusPager.NextPage(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to advance page") + } + for _, skuResult := range skuResults.Value { + if skuResult.Name != nil && skuResult.ResourceType != nil && *skuResult.ResourceType == "disks" { + s.mapLocationToZone(skuResult, ®ionMap) + } + } + } // convert to map of []string regionMapResult := make(map[string][]string) for region, zoneSet := range regionMap { @@ -750,3 +788,21 @@ func (s *AdStorage) dynamicRegionMapAzure(ctx context.Context) (map[string][]str } return regionMapResult, nil } + +func (s *AdStorage) mapLocationToZone(skuResult *armcompute.ResourceSKU, regionMap *map[string]LocationZoneMap) { + var rm = *regionMap + for _, locationInfo := range skuResult.LocationInfo { + location := "" + if locationInfo.Location != nil { + location = *locationInfo.Location + } else { + continue + } + if val, ok := rm[location]; ok { + for _, zone := range locationInfo.Zones { + val[*zone] = struct{}{} + } + rm[location] = val + } + } +} diff --git a/pkg/blockstorage/azure/client.go b/pkg/blockstorage/azure/client.go index 86589b8af6..79c5413f7d 100644 --- a/pkg/blockstorage/azure/client.go +++ b/pkg/blockstorage/azure/client.go @@ -21,26 +21,34 @@ package azure import ( "context" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/azure/auth" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions" + "github.com/pkg/errors" + "github.com/kanisterio/kanister/pkg/blockstorage" "github.com/kanisterio/kanister/pkg/log" - "github.com/pkg/errors" ) -// Client is a wrapper for Client client +// Client is a wrapper type Client struct { - SubscriptionID string - ResourceGroup string - BaseURI string - Authorizer *autorest.BearerAuthorizer - DisksClient *compute.DisksClient - SnapshotsClient *compute.SnapshotsClient + Cred azcore.TokenCredential + SubscriptionID string + ResourceGroup string + BaseURI string + DisksClient *armcompute.DisksClient + SnapshotsClient *armcompute.SnapshotsClient + SKUsClient *armcompute.ResourceSKUsClient + SubscriptionsClient *armsubscriptions.Client } // NewClient returns a Client struct +var ( + computeClientFactory *armcompute.ClientFactory + subscriptionsClientFactory *armsubscriptions.ClientFactory +) + func NewClient(ctx context.Context, config map[string]string) (*Client, error) { var resourceGroup string var subscriptionID string @@ -65,94 +73,62 @@ func NewClient(ctx context.Context, config map[string]string) (*Client, error) { } } - if id, ok := config[blockstorage.AzureCloudEnvironmentID]; !ok || id == "" { - config[blockstorage.AzureCloudEnvironmentID] = azure.PublicCloud.Name - } - - env, err := azure.EnvironmentFromName(config[blockstorage.AzureCloudEnvironmentID]) + authenticator, err := NewAzureAuthenticator(config) if err != nil { - return nil, errors.Wrap(err, "Failed to fetch the cloud environment.") + return nil, err } - - authorizer, err := getAuthorizer(env, config) + err = authenticator.Authenticate(config) if err != nil { return nil, err } - - _, ok = config[blockstorage.AzureResurceMgrEndpoint] - if !ok { - config[blockstorage.AzureResurceMgrEndpoint] = env.ResourceManagerEndpoint + cred := authenticator.GetAuthorizer() + computeClientFactory, err = armcompute.NewClientFactory(subscriptionID, cred, nil) + if err != nil { + return nil, err } - disksClient := compute.NewDisksClientWithBaseURI(config[blockstorage.AzureResurceMgrEndpoint], subscriptionID) - disksClient.Authorizer = authorizer - - snapshotsClient := compute.NewSnapshotsClientWithBaseURI(config[blockstorage.AzureResurceMgrEndpoint], subscriptionID) - snapshotsClient.Authorizer = authorizer - - return &Client{ - BaseURI: config[blockstorage.AzureResurceMgrEndpoint], - SubscriptionID: subscriptionID, - Authorizer: authorizer, - DisksClient: &disksClient, - SnapshotsClient: &snapshotsClient, - ResourceGroup: resourceGroup, - }, nil -} - -//nolint:unparam -func getAuthorizer(env azure.Environment, config map[string]string) (*autorest.BearerAuthorizer, error) { - if isClientCredsAvailable(config) { - return getClientCredsAuthorizer(env, config) - } else if isMSICredsAvailable(config) { - return getMSIsAuthorizer(config) - } - return nil, errors.New("Missing credentials, or credential type not supported") -} + subscriptionsClientFactory, err = armsubscriptions.NewClientFactory(cred, nil) -func getClientCredsAuthorizer(env azure.Environment, config map[string]string) (*autorest.BearerAuthorizer, error) { - credConfig, err := getCredConfig(env, config) if err != nil { - return nil, errors.Wrap(err, "Failed to get Azure Client Credentials Config") - } - a, err := credConfig.Authorizer() - if err != nil { - return nil, errors.Wrap(err, "Failed to get Azure Client Credentials authorizer") - } - ba, ok := a.(*autorest.BearerAuthorizer) - if !ok { - return nil, errors.New("Failed to get Azure authorizer") + return nil, err } - return ba, nil -} -func getMSIsAuthorizer(config map[string]string) (*autorest.BearerAuthorizer, error) { - msiConfig := auth.NewMSIConfig() - msiConfig.ClientID = config[blockstorage.AzureClientID] - a, err := msiConfig.Authorizer() + disksClient := computeClientFactory.NewDisksClient() + snapshotsClient := computeClientFactory.NewSnapshotsClient() + skusClient := computeClientFactory.NewResourceSKUsClient() + subscriptionsClient := subscriptionsClientFactory.NewClient() + if err != nil { - return nil, errors.Wrap(err, "Failed to get Azure MSI authorizer") - } - ba, ok := a.(*autorest.BearerAuthorizer) - if !ok { - return nil, errors.New("Failed to get Azure authorizer") + return nil, err } - return ba, nil + + return &Client{ + Cred: cred, + BaseURI: config[blockstorage.AzureResurceMgrEndpoint], + SubscriptionID: subscriptionID, + DisksClient: disksClient, + SnapshotsClient: snapshotsClient, + SKUsClient: skusClient, + SubscriptionsClient: subscriptionsClient, + ResourceGroup: resourceGroup, + }, nil } -func getCredConfig(env azure.Environment, config map[string]string) (auth.ClientCredentialsConfig, error) { +func getCredConfig(env Environment, config map[string]string) (ClientCredentialsConfig, error) { credConfig, err := getCredConfigForAuth(config) if err != nil { - return auth.ClientCredentialsConfig{}, err + return ClientCredentialsConfig{}, err } + + //Todo: Find alternatives to azure.Environment var ok bool if credConfig.AADEndpoint, ok = config[blockstorage.AzureActiveDirEndpoint]; !ok || credConfig.AADEndpoint == "" { - credConfig.AADEndpoint = env.ActiveDirectoryEndpoint + credConfig.AADEndpoint = env.Configuration.ActiveDirectoryAuthorityHost config[blockstorage.AzureActiveDirEndpoint] = credConfig.AADEndpoint } if credConfig.Resource, ok = config[blockstorage.AzureActiveDirResourceID]; !ok || credConfig.Resource == "" { - credConfig.Resource = env.ResourceManagerEndpoint + credConfig.Resource = env.Configuration.Services[cloud.ResourceManager].Endpoint config[blockstorage.AzureActiveDirResourceID] = credConfig.Resource } diff --git a/pkg/blockstorage/azure/client_test.go b/pkg/blockstorage/azure/client_test.go index a49c1be597..72970dc6f5 100644 --- a/pkg/blockstorage/azure/client_test.go +++ b/pkg/blockstorage/azure/client_test.go @@ -20,11 +20,11 @@ import ( "strings" "testing" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/azure/auth" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" + . "gopkg.in/check.v1" + "github.com/kanisterio/kanister/pkg/blockstorage" envconfig "github.com/kanisterio/kanister/pkg/config" - . "gopkg.in/check.v1" ) // Hook up gocheck into the "go test" runner. @@ -48,12 +48,13 @@ func (s *ClientSuite) TestClient(c *C) { config[blockstorage.AzureCloudEnvironmentID] = envconfig.GetEnvOrSkip(c, blockstorage.AzureCloudEnvironmentID) azCli, err := NewClient(context.Background(), config) c.Assert(err, IsNil) - + c.Assert(azCli.Cred, NotNil) c.Assert(azCli.SubscriptionID, NotNil) - c.Assert(azCli.Authorizer, NotNil) c.Assert(azCli.DisksClient, NotNil) c.Assert(azCli.SnapshotsClient, NotNil) - _, err = azCli.DisksClient.List(context.Background()) + c.Assert(azCli.DisksClient.NewListPager(nil), NotNil) + c.Assert(azCli.SKUsClient, NotNil) + c.Assert(azCli.SubscriptionsClient, NotNil) c.Assert(err, IsNil) } @@ -87,13 +88,15 @@ func (s ClientSuite) TestGetRegions(c *C) { func (s *ClientSuite) TestGetCredConfig(c *C) { for _, tc := range []struct { - env azure.Environment + name string + env Environment config map[string]string errChecker Checker - expCCC auth.ClientCredentialsConfig + expCCC ClientCredentialsConfig }{ { - env: azure.PublicCloud, + name: "Test with all attributes in configuration", + env: PublicCloud, config: map[string]string{ blockstorage.AzureTenantID: "atid", blockstorage.AzureClientID: "acid", @@ -101,7 +104,7 @@ func (s *ClientSuite) TestGetCredConfig(c *C) { blockstorage.AzureActiveDirEndpoint: "aade", blockstorage.AzureActiveDirResourceID: "aadrid", }, - expCCC: auth.ClientCredentialsConfig{ + expCCC: ClientCredentialsConfig{ ClientID: "acid", ClientSecret: "acs", TenantID: "atid", @@ -111,23 +114,25 @@ func (s *ClientSuite) TestGetCredConfig(c *C) { errChecker: IsNil, }, { - env: azure.PublicCloud, + name: "Test with client credential in configuration", + env: PublicCloud, config: map[string]string{ blockstorage.AzureTenantID: "atid", blockstorage.AzureClientID: "acid", blockstorage.AzureClientSecret: "acs", }, - expCCC: auth.ClientCredentialsConfig{ + expCCC: ClientCredentialsConfig{ ClientID: "acid", ClientSecret: "acs", TenantID: "atid", - Resource: azure.PublicCloud.ResourceManagerEndpoint, - AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint, + Resource: cloud.AzurePublic.Services[cloud.ResourceManager].Endpoint, + AADEndpoint: cloud.AzurePublic.ActiveDirectoryAuthorityHost, }, errChecker: IsNil, }, { - env: azure.USGovernmentCloud, + name: "Test without AD in configuration", + env: USGovernmentCloud, config: map[string]string{ blockstorage.AzureTenantID: "atid", blockstorage.AzureClientID: "acid", @@ -135,17 +140,18 @@ func (s *ClientSuite) TestGetCredConfig(c *C) { blockstorage.AzureActiveDirEndpoint: "", blockstorage.AzureActiveDirResourceID: "", }, - expCCC: auth.ClientCredentialsConfig{ + expCCC: ClientCredentialsConfig{ ClientID: "acid", ClientSecret: "acs", TenantID: "atid", - Resource: azure.USGovernmentCloud.ResourceManagerEndpoint, - AADEndpoint: azure.USGovernmentCloud.ActiveDirectoryEndpoint, + Resource: cloud.AzureGovernment.Services[cloud.ResourceManager].Endpoint, + AADEndpoint: cloud.AzureGovernment.ActiveDirectoryAuthorityHost, }, errChecker: IsNil, }, { - env: azure.USGovernmentCloud, + name: "Test with tenantid and clientid in configuration", + env: USGovernmentCloud, config: map[string]string{ blockstorage.AzureTenantID: "atid", blockstorage.AzureClientID: "acid", @@ -153,14 +159,16 @@ func (s *ClientSuite) TestGetCredConfig(c *C) { errChecker: NotNil, }, { - env: azure.USGovernmentCloud, + name: "Test with tenantid in configuration", + env: USGovernmentCloud, config: map[string]string{ blockstorage.AzureTenantID: "atid", }, errChecker: NotNil, }, { - env: azure.USGovernmentCloud, + name: "Test with nil configuration", + env: USGovernmentCloud, config: map[string]string{}, errChecker: NotNil, }, diff --git a/pkg/blockstorage/azure/environments.go b/pkg/blockstorage/azure/environments.go new file mode 100644 index 0000000000..ae8a1fbd5d --- /dev/null +++ b/pkg/blockstorage/azure/environments.go @@ -0,0 +1,98 @@ +// Copyright 2023 The Kanister Authors. +// +// 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. + +package azure + +import ( + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" +) + +const ( + // NotAvailable is used for endpoints and resource IDs that are not available for a given cloud. + NotAvailable = "N/A" +) + +var environments = map[string]Environment{ + "AZURECHINACLOUD": ChinaCloud, + "AZUREGERMANCLOUD": GermanCloud, + "AZURECLOUD": PublicCloud, + "AZUREPUBLICCLOUD": PublicCloud, + "AZUREUSGOVERNMENT": USGovernmentCloud, + "AZUREUSGOVERNMENTCLOUD": USGovernmentCloud, +} + +// Environment represents a set of endpoints for each of Azure's Clouds. +type Environment struct { + Name string `json:"name"` + ResourceManagerEndpoint string `json:"resourceManagerEndpoint"` + ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"` + StorageEndpointSuffix string `json:"storageEndpointSuffix"` + Configuration cloud.Configuration +} + +var ( + // PublicCloud is the default public Azure cloud environment + //Ref: https://github.com/Azure/azure-sdk-for-go/blob/sdk/storage/azblob/v0.4.0/eng/common/TestResources/clouds/AzureCloud.json + PublicCloud = Environment{ + Name: "AzurePublicCloud", + ResourceManagerEndpoint: "https://management.azure.com/", + ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", + StorageEndpointSuffix: "core.windows.net", + Configuration: cloud.AzurePublic, + } + + // USGovernmentCloud is the cloud environment for the US Government + //Ref: https://github.com/Azure/azure-sdk-for-go/blob/sdk/storage/azblob/v0.4.0/eng/common/TestResources/clouds/AzureUSGovernment.json + USGovernmentCloud = Environment{ + Name: "AzureUSGovernmentCloud", + ResourceManagerEndpoint: "https://management.usgovcloudapi.net/", + ActiveDirectoryEndpoint: "https://login.microsoftonline.us/", + StorageEndpointSuffix: "core.usgovcloudapi.net", + Configuration: cloud.AzureGovernment, + } + + // ChinaCloud is the cloud environment operated in China + //Ref: https://github.com/Azure/azure-sdk-for-go/blob/sdk/storage/azblob/v0.4.0/eng/common/TestResources/clouds/AzureChinaCloud.json + ChinaCloud = Environment{ + Name: "AzureChinaCloud", + ResourceManagerEndpoint: "https://management.chinacloudapi.cn/", + ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/", + StorageEndpointSuffix: "core.chinacloudapi.cn", + Configuration: cloud.AzureChina, + } + + // GermanCloud is the cloud environment operated in Germany has been deprecated + // Ref: https://learn.microsoft.com/en-us/previous-versions/azure/germany/germany-welcome + GermanCloud = Environment{ + Name: "AzureGermanCloud", + ResourceManagerEndpoint: NotAvailable, + ActiveDirectoryEndpoint: NotAvailable, + StorageEndpointSuffix: NotAvailable, + Configuration: cloud.Configuration{}, + } +) + +// EnvironmentFromName returns an Environment based on the common name specified. +func EnvironmentFromName(name string) (Environment, error) { + name = strings.ToUpper(name) + env, ok := environments[name] + if !ok { + return env, fmt.Errorf("environment/azure: There is no cloud environment matching the name %q", name) + } + + return env, nil +} diff --git a/pkg/blockstorage/helper_test.go b/pkg/blockstorage/helper_test.go new file mode 100644 index 0000000000..13e0a1dfd8 --- /dev/null +++ b/pkg/blockstorage/helper_test.go @@ -0,0 +1,60 @@ +// Copyright 2023 The Kanister Authors. +// +// 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. + +package blockstorage + +import ( + . "gopkg.in/check.v1" +) + +type HelperSuite struct{} + +var _ = Suite(&HelperSuite{}) + +func (s *HelperSuite) SetUpSuite(c *C) { +} + +func (h *HelperSuite) TestStringSlice(c *C) { + source := []string{"test1", "test2"} + target := StringSlice(&source) + c.Assert(target[0], Equals, source[0]) + c.Assert(target[1], Equals, source[1]) +} + +func (s *HelperSuite) TestSliceStringPtr(c *C) { + source := []string{"test1", "test2"} + res := SliceStringPtr(source) + for i, elePtr := range res { + var target = *elePtr + c.Assert(target, Equals, source[i]) + } +} + +func (s *HelperSuite) TestIntFromPtr(c *C) { + source := 1 + target := Int(&source) + c.Assert(target, Equals, source) +} + +func (s *HelperSuite) TestIntToPtr(c *C) { + source := 1 + target := IntPtr(source) + c.Assert(*target, Equals, source) +} + +func (s *HelperSuite) TestStringToPtr(c *C) { + source := "test" + target := StringPtr(source) + c.Assert(*target, Equals, source) +} diff --git a/pkg/blockstorage/helpers.go b/pkg/blockstorage/helpers.go index 20ea680d19..ae28929b2f 100644 --- a/pkg/blockstorage/helpers.go +++ b/pkg/blockstorage/helpers.go @@ -17,6 +17,8 @@ package blockstorage import ( "bytes" + azto "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + ktags "github.com/kanisterio/kanister/pkg/blockstorage/tags" ) @@ -105,3 +107,94 @@ func FilterSnapshotsWithTags(snapshots []*Snapshot, tags map[string]string) []*S } return result } + +// utility functions equivalent to old functions from package `go-autorest/autorest/to` + +// StringMapPtr returns a pointer to a map of string pointers built from the passed map of strings. +func StringMapPtr(ms map[string]string) *map[string]*string { + msp := make(map[string]*string, len(ms)) + for k, s := range ms { + msp[k] = azto.Ptr(s) + } + return &msp +} + +// StringMap returns a map of strings built from the map of string pointers. The empty string is +// used for nil pointers. +func StringMap(msp map[string]*string) map[string]string { + ms := make(map[string]string, len(msp)) + for k, sp := range msp { + if sp != nil { + ms[k] = *sp + } else { + ms[k] = "" + } + } + return ms +} + +// StringSlice returns a string slice value for the passed string slice pointer. It returns a nil +// slice if the pointer is nil. +func StringSlice(s *[]string) []string { + if s != nil { + return *s + } + return nil +} + +// SliceStringPtr returns a slice of string pointers from the passed string slice. +func SliceStringPtr(s []string) []*string { + ms := make([]*string, len(s)) + for k, sp := range s { + ms[k] = azto.Ptr(sp) + } + return ms +} + +// Int returns an int value for the passed int pointer. It returns 0 if the pointer is nil. +func Int(i *int) int { + if i != nil { + return *i + } + return 0 +} + +// IntPtr returns a pointer to the passed int. +func IntPtr(i int) *int { + return &i +} + +// Int32 returns an int value for the passed int pointer. It returns 0 if the pointer is nil. +func Int32(i *int32) int32 { + if i != nil { + return *i + } + return 0 +} + +// Int32Ptr returns a pointer to the passed int32. +func Int32Ptr(i int32) *int32 { + return &i +} + +// Int64 returns an int value for the passed int pointer. It returns 0 if the pointer is nil. +func Int64(i *int64) int64 { + if i != nil { + return *i + } + return 0 +} + +// String returns a string value for the passed string pointer. It returns the empty string if the +// pointer is nil. +func StringFromPtr(s *string) string { + if s != nil { + return *s + } + return "" +} + +// StringPtr returns a pointer to the passed string. +func StringPtr(s string) *string { + return &s +} diff --git a/pkg/kopia/command/storage/secret_utils.go b/pkg/kopia/command/storage/secret_utils.go index 1935f7354b..0f4b6add95 100644 --- a/pkg/kopia/command/storage/secret_utils.go +++ b/pkg/kopia/command/storage/secret_utils.go @@ -18,12 +18,12 @@ import ( "context" "time" - "github.com/Azure/go-autorest/autorest/azure" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1" "github.com/kanisterio/kanister/pkg/aws" + "github.com/kanisterio/kanister/pkg/blockstorage/azure" "github.com/kanisterio/kanister/pkg/secrets" "github.com/kanisterio/kanister/pkg/secrets/repositoryserver" )