From 005a6f67c6a8fd7de45c181add7512fcd2401d4b Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Mon, 28 Jul 2025 13:29:22 -0700 Subject: [PATCH 1/7] Azure WIP * able to start a container on Azure --- defang.code-workspace | 36 +- flake.nix | 1 + src/go.mod | 19 +- src/go.sum | 54 ++- src/pkg/cli/client/byoc/aws/byoc.go | 21 +- src/pkg/cli/client/byoc/azure/byoc.go | 145 ++++++ src/pkg/cli/client/byoc/do/stream.go | 2 +- src/pkg/cli/client/byoc/gcp/byoc.go | 12 +- src/pkg/cli/client/provider_id.go | 6 +- src/pkg/cli/client/region.go | 2 + src/pkg/clouds/aws/common.go | 6 - src/pkg/clouds/aws/ecs/cfn/setup.go | 4 +- src/pkg/clouds/aws/ecs/cfn/setup_test.go | 4 +- src/pkg/clouds/aws/ecs/cfn/template.go | 19 +- src/pkg/clouds/aws/ecs/logs.go | 7 +- src/pkg/clouds/aws/ecs/status.go | 3 +- src/pkg/clouds/aws/ecs/tail.go | 4 +- src/pkg/clouds/aws/region.go | 53 +++ src/pkg/clouds/aws/region/region.go | 51 --- src/pkg/clouds/azure/aci/common.go | 80 ++++ src/pkg/clouds/azure/aci/common_test.go | 19 + src/pkg/clouds/azure/aci/run.go | 69 +++ src/pkg/clouds/azure/aci/run_test.go | 43 ++ src/pkg/clouds/azure/aci/setup.go | 93 ++++ src/pkg/clouds/azure/aci/setup_test.go | 42 ++ src/pkg/clouds/azure/aci/stop.go | 18 + src/pkg/clouds/azure/aci/tail.go | 127 ++++++ src/pkg/clouds/azure/aci/tail_test.go | 57 +++ src/pkg/clouds/azure/common.go | 5 + src/pkg/clouds/azure/job.go | 32 ++ src/pkg/clouds/azure/location.go | 120 +++++ src/pkg/clouds/gcp/cloudrun.go | 4 +- src/pkg/cmd/factory.go | 66 +-- src/pkg/types/driver.go | 26 +- src/protos/io/defang/v1/fabric.pb.go | 554 ++++++++++++----------- src/protos/io/defang/v1/fabric.proto | 88 ++-- 36 files changed, 1391 insertions(+), 501 deletions(-) create mode 100644 src/pkg/cli/client/byoc/azure/byoc.go create mode 100644 src/pkg/clouds/aws/region.go delete mode 100644 src/pkg/clouds/aws/region/region.go create mode 100644 src/pkg/clouds/azure/aci/common.go create mode 100644 src/pkg/clouds/azure/aci/common_test.go create mode 100644 src/pkg/clouds/azure/aci/run.go create mode 100644 src/pkg/clouds/azure/aci/run_test.go create mode 100644 src/pkg/clouds/azure/aci/setup.go create mode 100644 src/pkg/clouds/azure/aci/setup_test.go create mode 100644 src/pkg/clouds/azure/aci/stop.go create mode 100644 src/pkg/clouds/azure/aci/tail.go create mode 100644 src/pkg/clouds/azure/aci/tail_test.go create mode 100644 src/pkg/clouds/azure/common.go create mode 100644 src/pkg/clouds/azure/job.go create mode 100644 src/pkg/clouds/azure/location.go diff --git a/defang.code-workspace b/defang.code-workspace index 0c81507e6..72d3f4942 100644 --- a/defang.code-workspace +++ b/defang.code-workspace @@ -1,19 +1,19 @@ { - "folders": [ - { - "path": "." - }, - { - "path": "src" - } - ], - "settings": { - "go.testEnvVars": { - "AWS_PROFILE": "defang-sandbox" - }, - "go.buildTags": "integration", - "go.testFlags": ["-short"], - "go.testTimeout": "300s", - "makefile.configureOnOpen": false - } -} \ No newline at end of file + "folders": [ + { + "path": "." + }, + { + "path": "src" + } + ], + "settings": { + "go.testEnvVars": { + "AWS_PROFILE": "defang-sandbox" + }, + "go.buildTags": "integration", + "go.testFlags": ["-short", "-v"], + "go.testTimeout": "300s", + "makefile.configureOnOpen": false + } +} diff --git a/flake.nix b/flake.nix index 6327289d8..8f0c2dfc0 100644 --- a/flake.nix +++ b/flake.nix @@ -18,6 +18,7 @@ mkShell { buildInputs = [ + azure-cli buf crane git diff --git a/src/go.mod b/src/go.mod index c74693378..0afcb5e98 100644 --- a/src/go.mod +++ b/src/go.mod @@ -16,6 +16,10 @@ require ( cloud.google.com/go/secretmanager v1.14.5 cloud.google.com/go/storage v1.50.0 github.com/AlecAivazis/survey/v2 v2.3.7 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0 github.com/DefangLabs/secret-detector v0.0.0-20250108223530-c2b44d4c1f8f github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 github.com/aws/aws-sdk-go-v2 v1.32.6 @@ -53,8 +57,8 @@ require ( github.com/spf13/pflag v1.0.6 golang.org/x/mod v0.18.0 golang.org/x/oauth2 v0.29.0 - golang.org/x/sys v0.32.0 - golang.org/x/term v0.31.0 + golang.org/x/sys v0.33.0 + golang.org/x/term v0.32.0 google.golang.org/api v0.229.0 google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb google.golang.org/grpc v1.72.0 @@ -70,6 +74,8 @@ require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/longrunning v0.6.6 // indirect cloud.google.com/go/monitoring v1.24.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect @@ -91,6 +97,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/morikuni/aec v1.0.0 // indirect @@ -109,8 +116,8 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect gopkg.in/ini.v1 v1.66.2 // indirect @@ -155,8 +162,8 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect golang.org/x/tools v0.22.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index fe6b07db6..ddba8959a 100644 --- a/src/go.sum +++ b/src/go.sum @@ -32,8 +32,30 @@ cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0 h1:+dIXMjlifRbG3d01DF8dwckUSXADuW5dgBNt1fbkpv0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0/go.mod h1:FN0UJ15tJ7kV7JYrYAleEq44Ew1cUiyLcJrfrTxHGd0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.2.0 h1:akP6VpxJGgQRpDR1P462piz/8OhYLRCreDj48AyNabc= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.2.0/go.mod h1:8wzvopPfyZYPaQUoKW87Zfdul7jmJMDfp/k7YY3oJyA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0 h1:seyVIpxalxYmfjoo8MB4rRzWaobMG+KJ2+MAUrEvDGU= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0/go.mod h1:M3QD7IyKZBaC4uAKjitTOSOXdcPC6JS1A9oOW3hYjbQ= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/DefangLabs/cobra v1.8.0-defang h1:rTzAg1XbEk3yXUmQPumcwkLgi8iNCby5CjyG3sCwzKk= github.com/DefangLabs/cobra v1.8.0-defang/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/DefangLabs/secret-detector v0.0.0-20250108223530-c2b44d4c1f8f h1:RTbUqLhPxejgK92ifVdMTIW9H23QLlscy8QXPDTfaL4= @@ -127,6 +149,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/digitalocean/godo v1.131.1 h1:2QsRwjNukKgOQbflMxOsTDoC05o5UKBpqQMFKXegYKE= github.com/digitalocean/godo v1.131.1/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= @@ -215,6 +239,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -224,6 +250,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mark3labs/mcp-go v0.21.0 h1:oyEtiXg8PnrVEFis9b1AwbiUWF2dTbyBP5yLo7SruXE= @@ -269,6 +297,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -335,8 +365,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -348,16 +378,16 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -370,18 +400,18 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/src/pkg/cli/client/byoc/aws/byoc.go b/src/pkg/cli/client/byoc/aws/byoc.go index 04515a892..a54bebe8d 100644 --- a/src/pkg/cli/client/byoc/aws/byoc.go +++ b/src/pkg/cli/client/byoc/aws/byoc.go @@ -35,7 +35,6 @@ import ( s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/aws/smithy-go" - "github.com/aws/smithy-go/ptr" "github.com/bufbuild/connect-go" composeTypes "github.com/compose-spec/compose-go/v2/types" "google.golang.org/protobuf/proto" @@ -139,26 +138,24 @@ func (b *ByocAws) setUpCD(ctx context.Context) error { term.Debugf("Using CD image: %q", b.CDImage) - cdTaskName := byoc.CdTaskPrefix + cdContainerName := byoc.CdTaskPrefix containers := []types.Container{ { // FIXME: get the Pulumi image or version from Fabric: https://github.com/DefangLabs/defang/issues/1027 - Image: "public.ecr.aws/pulumi/pulumi-nodejs:" + b.PulumiVersion, - Name: ecs.CdContainerName, - Cpus: 2.0, - Memory: 2048_000_000, // 2G - Essential: ptr.Bool(true), + Image: "public.ecr.aws/pulumi/pulumi-nodejs:" + b.PulumiVersion, + Name: ecs.CdContainerName, + Cpus: 2.0, + Memory: 2048_000_000, // 2G VolumesFrom: []string{ - cdTaskName, + cdContainerName, }, WorkDir: "/app", - DependsOn: map[string]types.ContainerCondition{cdTaskName: "START"}, EntryPoint: []string{"node", "lib/index.js"}, }, { - Image: b.CDImage, - Name: cdTaskName, - Essential: ptr.Bool(false), + Image: b.CDImage, + Name: cdContainerName, + IsInit: true, Volumes: []types.TaskVolume{ { Source: "pulumi-plugins", diff --git a/src/pkg/cli/client/byoc/azure/byoc.go b/src/pkg/cli/client/byoc/azure/byoc.go new file mode 100644 index 000000000..cf16f9c56 --- /dev/null +++ b/src/pkg/cli/client/byoc/azure/byoc.go @@ -0,0 +1,145 @@ +package azure + +import ( + "context" + + "github.com/DefangLabs/defang/src/pkg/cli/client" + "github.com/DefangLabs/defang/src/pkg/cli/client/byoc" + "github.com/DefangLabs/defang/src/pkg/clouds/azure/aci" + "github.com/DefangLabs/defang/src/pkg/types" + defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1" +) + +type ByocAzure struct { + *byoc.ByocBaseClient + + driver *aci.ContainerInstance +} + +var _ client.Provider = (*ByocAzure)(nil) + +func NewByocAzure(ctx context.Context, tenantName types.TenantName) *ByocAzure { + b := &ByocAzure{ + driver: aci.NewContainerInstance("defang-cd", ""), // default location => from AZURE_LOCATION env var + } + b.ByocBaseClient = byoc.NewByocBaseClient(ctx, tenantName, b) + return b +} + +// AccountInfo implements client.Provider. +func (b *ByocAzure) AccountInfo(context.Context) (*client.AccountInfo, error) { + panic("unimplemented") +} + +// BootstrapCommand implements client.Provider. +func (b *ByocAzure) BootstrapCommand(context.Context, client.BootstrapCommandRequest) (types.ETag, error) { + panic("unimplemented") +} + +// BootstrapList implements client.Provider. +func (b *ByocAzure) BootstrapList(context.Context) ([]string, error) { + panic("unimplemented") +} + +// CreateUploadURL implements client.Provider. +func (b *ByocAzure) CreateUploadURL(context.Context, *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) { + panic("unimplemented") +} + +// Delete implements client.Provider. +func (b *ByocAzure) Delete(context.Context, *defangv1.DeleteRequest) (*defangv1.DeleteResponse, error) { + panic("unimplemented") +} + +// DeleteConfig implements client.Provider. +func (b *ByocAzure) DeleteConfig(context.Context, *defangv1.Secrets) error { + panic("unimplemented") +} + +// Deploy implements client.Provider. +func (b *ByocAzure) Deploy(context.Context, *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { + panic("unimplemented") +} + +// Destroy implements client.Provider. +func (b *ByocAzure) Destroy(context.Context, *defangv1.DestroyRequest) (types.ETag, error) { + panic("unimplemented") +} + +// GetDeploymentStatus implements client.Provider. +func (b *ByocAzure) GetDeploymentStatus(context.Context) error { + panic("unimplemented") +} + +// GetProjectUpdate implements client.Provider. +func (b *ByocAzure) GetProjectUpdate(context.Context, string) (*defangv1.ProjectUpdate, error) { + panic("unimplemented") +} + +// GetService implements client.Provider. +func (b *ByocAzure) GetService(context.Context, *defangv1.GetRequest) (*defangv1.ServiceInfo, error) { + panic("unimplemented") +} + +// GetServices implements client.Provider. +func (b *ByocAzure) GetServices(context.Context, *defangv1.GetServicesRequest) (*defangv1.GetServicesResponse, error) { + panic("unimplemented") +} + +// ListConfig implements client.Provider. +func (b *ByocAzure) ListConfig(context.Context, *defangv1.ListConfigsRequest) (*defangv1.Secrets, error) { + panic("unimplemented") +} + +// PrepareDomainDelegation implements client.Provider. +func (b *ByocAzure) PrepareDomainDelegation(context.Context, client.PrepareDomainDelegationRequest) (*client.PrepareDomainDelegationResponse, error) { + panic("unimplemented") +} + +// Preview implements client.Provider. +func (b *ByocAzure) Preview(context.Context, *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { + panic("unimplemented") +} + +// PutConfig implements client.Provider. +func (b *ByocAzure) PutConfig(context.Context, *defangv1.PutConfigRequest) error { + panic("unimplemented") +} + +// QueryForDebug implements client.Provider. +func (b *ByocAzure) QueryForDebug(context.Context, *defangv1.DebugRequest) error { + panic("unimplemented") +} + +// QueryLogs implements client.Provider. +func (b *ByocAzure) QueryLogs(context.Context, *defangv1.TailRequest) (client.ServerStream[defangv1.TailResponse], error) { + panic("unimplemented") +} + +// RemoteProjectName implements client.Provider. +// Subtle: this method shadows the method (*ByocBaseClient).RemoteProjectName of ByocAzure.ByocBaseClient. +func (b *ByocAzure) RemoteProjectName(context.Context) (string, error) { + panic("unimplemented") +} + +// ServiceDNS implements client.Provider. +// Subtle: this method shadows the method (*ByocBaseClient).ServiceDNS of ByocAzure.ByocBaseClient. +func (b *ByocAzure) ServiceDNS(string) string { + panic("unimplemented") +} + +// SetCanIUseConfig implements client.Provider. +// Subtle: this method shadows the method (*ByocBaseClient).SetCanIUseConfig of ByocAzure.ByocBaseClient. +func (b *ByocAzure) SetCanIUseConfig(*defangv1.CanIUseResponse) { + panic("unimplemented") +} + +// Subscribe implements client.Provider. +func (b *ByocAzure) Subscribe(context.Context, *defangv1.SubscribeRequest) (client.ServerStream[defangv1.SubscribeResponse], error) { + panic("unimplemented") +} + +// TearDown implements client.Provider. +func (b *ByocAzure) TearDown(ctx context.Context) error { + return b.driver.TearDown(ctx) +} diff --git a/src/pkg/cli/client/byoc/do/stream.go b/src/pkg/cli/client/byoc/do/stream.go index 45d2b7c67..aa6f51555 100644 --- a/src/pkg/cli/client/byoc/do/stream.go +++ b/src/pkg/cli/client/byoc/do/stream.go @@ -42,7 +42,7 @@ func newByocServerStream(ctx context.Context, liveUrl string, etag types.ETag) ( liveURL.Scheme = "wss" } - conn, _, err := websocket.DefaultDialer.DialContext(ctx, liveURL.String(), nil) + conn, _, err := websocket.DefaultDialer.DialContext(ctx, liveURL.String(), nil) // TODO: should we close resp.Body? if err != nil { return nil, err } diff --git a/src/pkg/cli/client/byoc/gcp/byoc.go b/src/pkg/cli/client/byoc/gcp/byoc.go index bc4551077..87a2466be 100644 --- a/src/pkg/cli/client/byoc/gcp/byoc.go +++ b/src/pkg/cli/client/byoc/gcp/byoc.go @@ -29,7 +29,6 @@ import ( "github.com/DefangLabs/defang/src/pkg/term" "github.com/DefangLabs/defang/src/pkg/types" defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1" - "github.com/aws/smithy-go/ptr" "github.com/bufbuild/connect-go" "google.golang.org/api/googleapi" auditpb "google.golang.org/genproto/googleapis/cloud/audit" @@ -225,12 +224,11 @@ func (b *ByocGcp) setUpCD(ctx context.Context) error { serviceAccount := path.Base(b.cdServiceAccount) if err := b.driver.SetupJob(ctx, "defang-cd", serviceAccount, []types.Container{ { - Image: b.CDImage, - Name: ecs.CdContainerName, - Cpus: 2.0, - Memory: 2048_000_000, // 2G - Essential: ptr.Bool(true), - WorkDir: "/app", + Image: b.CDImage, + Name: ecs.CdContainerName, + Cpus: 2.0, + Memory: 2048_000_000, // 2G + WorkDir: "/app", }, }); err != nil { return err diff --git a/src/pkg/cli/client/provider_id.go b/src/pkg/cli/client/provider_id.go index a69c6df9d..5fe9232b9 100644 --- a/src/pkg/cli/client/provider_id.go +++ b/src/pkg/cli/client/provider_id.go @@ -15,7 +15,7 @@ const ( ProviderAWS ProviderID = "aws" ProviderDO ProviderID = "digitalocean" ProviderGCP ProviderID = "gcp" - // ProviderAzure ProviderID = "azure" + ProviderAzure ProviderID = "azure" ) var allProviders = []ProviderID{ @@ -24,7 +24,7 @@ var allProviders = []ProviderID{ ProviderAWS, ProviderDO, ProviderGCP, - // ProviderAzure, + ProviderAzure, } func AllProviders() []ProviderID { @@ -89,6 +89,8 @@ func (p *ProviderID) SetValue(val defangv1.Provider) { *p = ProviderDO case defangv1.Provider_GCP: *p = ProviderGCP + case defangv1.Provider_AZURE: + *p = ProviderAzure default: *p = ProviderAuto } diff --git a/src/pkg/cli/client/region.go b/src/pkg/cli/client/region.go index 06844fd97..7d9a1559c 100644 --- a/src/pkg/cli/client/region.go +++ b/src/pkg/cli/client/region.go @@ -10,6 +10,8 @@ func GetRegion(provider ProviderID) string { return pkg.Getenv("CLOUDSDK_COMPUTE_REGION", "us-central1") // Default region for GCP case ProviderDO: return pkg.Getenv("DO_REGION", "nyc3") // Default region for DigitalOcean + case ProviderAzure: + return pkg.Getenv("AZURE_LOCATION", "westus") // Default region for Azure default: return "" // No default region for unsupported providers } diff --git a/src/pkg/clouds/aws/common.go b/src/pkg/clouds/aws/common.go index 660e268f2..5bde7fb01 100644 --- a/src/pkg/clouds/aws/common.go +++ b/src/pkg/clouds/aws/common.go @@ -11,16 +11,10 @@ import ( "github.com/aws/aws-sdk-go-v2/credentials/processcreds" ) -type Region string - type Aws struct { Region Region } -func (r Region) String() string { - return string(r) -} - func (a *Aws) LoadConfig(ctx context.Context) (aws.Config, error) { cfg, err := LoadDefaultConfig(ctx, a.Region) if err != nil { diff --git a/src/pkg/clouds/aws/ecs/cfn/setup.go b/src/pkg/clouds/aws/ecs/cfn/setup.go index 0a220087b..9e27022b9 100644 --- a/src/pkg/clouds/aws/ecs/cfn/setup.go +++ b/src/pkg/clouds/aws/ecs/cfn/setup.go @@ -8,10 +8,10 @@ import ( "strings" "time" + "github.com/DefangLabs/defang/src/pkg/clouds/aws" common "github.com/DefangLabs/defang/src/pkg/clouds/aws" "github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs" "github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs/cfn/outputs" - "github.com/DefangLabs/defang/src/pkg/clouds/aws/region" "github.com/DefangLabs/defang/src/pkg/types" "github.com/aws/aws-sdk-go-v2/service/cloudformation" cfnTypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" @@ -37,7 +37,7 @@ func OptionVPCAndSubnetID(ctx context.Context, vpcID, subnetID string) func(type } } -func New(stack string, region region.Region) *AwsEcs { +func New(stack string, region aws.Region) *AwsEcs { if stack == "" { panic("stack must be set") } diff --git a/src/pkg/clouds/aws/ecs/cfn/setup_test.go b/src/pkg/clouds/aws/ecs/cfn/setup_test.go index 60022754a..05f399c16 100644 --- a/src/pkg/clouds/aws/ecs/cfn/setup_test.go +++ b/src/pkg/clouds/aws/ecs/cfn/setup_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/DefangLabs/defang/src/pkg" - "github.com/DefangLabs/defang/src/pkg/clouds/aws/region" + "github.com/DefangLabs/defang/src/pkg/clouds/aws" "github.com/DefangLabs/defang/src/pkg/types" ) @@ -21,7 +21,7 @@ func TestCloudFormation(t *testing.T) { retainBucket = false // delete bucket after test user := pkg.GetCurrentUser() // avoid conflict with other users in the same account - aws := New("crun-test-"+user, region.Region("us-west-2")) + aws := New("crun-test-"+user, aws.RegionUSWest2) if aws == nil { t.Fatal("aws is nil") } diff --git a/src/pkg/clouds/aws/ecs/cfn/template.go b/src/pkg/clouds/aws/ecs/cfn/template.go index 6a67c9950..fa9699382 100644 --- a/src/pkg/clouds/aws/ecs/cfn/template.go +++ b/src/pkg/clouds/aws/ecs/cfn/template.go @@ -377,20 +377,23 @@ func createTemplate(stack string, containers []types.Container, overrides Templa } var dependsOn []ecs.TaskDefinition_ContainerDependency - if container.DependsOn != nil { - for name, condition := range container.DependsOn { - dependsOn = append(dependsOn, ecs.TaskDefinition_ContainerDependency{ - Condition: ptr.String(string(condition)), - ContainerName: ptr.String(name), - }) + if !container.IsInit { + // Add COMPLETE dependencies to any init-containers + for _, c := range containers { + if c.IsInit { + dependsOn = append(dependsOn, ecs.TaskDefinition_ContainerDependency{ + Condition: ptr.String("COMPLETE"), + ContainerName: ptr.String(c.Name), + }) + } } } def := ecs.TaskDefinition_ContainerDefinition{ Name: name, Image: images[i], - StopTimeout: ptr.Int(120), // TODO: make this configurable - Essential: container.Essential, + StopTimeout: ptr.Int(120), // TODO: make this configurable + Essential: ptr.Bool(!container.IsInit), // init containers are not essential Cpu: cpuShares, LogConfiguration: &ecs.TaskDefinition_LogConfiguration{ LogDriver: "awslogs", diff --git a/src/pkg/clouds/aws/ecs/logs.go b/src/pkg/clouds/aws/ecs/logs.go index 12f6e731d..75d3223d7 100644 --- a/src/pkg/clouds/aws/ecs/logs.go +++ b/src/pkg/clouds/aws/ecs/logs.go @@ -9,7 +9,6 @@ import ( "time" "github.com/DefangLabs/defang/src/pkg/clouds/aws" - "github.com/DefangLabs/defang/src/pkg/clouds/aws/region" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" "github.com/aws/smithy-go/ptr" @@ -90,8 +89,8 @@ func TailLogGroup(ctx context.Context, input LogGroupInput) (LiveTailStream, err LogEventFilterPattern: pattern, } - region := region.FromArn(slti.LogGroupIdentifiers[0]) // must have at least one log group - cw, err := newCloudWatchLogsClient(ctx, region) // assume all log groups are in the same region + region := aws.RegionFromArn(slti.LogGroupIdentifiers[0]) // must have at least one log group + cw, err := newCloudWatchLogsClient(ctx, region) // assume all log groups are in the same region if err != nil { return nil, err } @@ -129,7 +128,7 @@ func QueryLogGroups(ctx context.Context, start, end time.Time, logGroups ...LogG } func QueryLogGroup(ctx context.Context, input LogGroupInput, start, end time.Time, cb func([]LogEvent) error) error { - region := region.FromArn(input.LogGroupARN) + region := aws.RegionFromArn(input.LogGroupARN) cw, err := newCloudWatchLogsClient(ctx, region) if err != nil { return err diff --git a/src/pkg/clouds/aws/ecs/status.go b/src/pkg/clouds/aws/ecs/status.go index c011a2713..87484a474 100644 --- a/src/pkg/clouds/aws/ecs/status.go +++ b/src/pkg/clouds/aws/ecs/status.go @@ -8,14 +8,13 @@ import ( "time" "github.com/DefangLabs/defang/src/pkg/clouds/aws" - "github.com/DefangLabs/defang/src/pkg/clouds/aws/region" "github.com/aws/aws-sdk-go-v2/service/ecs" ecsTypes "github.com/aws/aws-sdk-go-v2/service/ecs/types" ) // GetTaskStatus returns nil if the task is still running, io.EOF if the task is stopped successfully, or an error if the task failed. func GetTaskStatus(ctx context.Context, taskArn TaskArn) error { - region := region.FromArn(*taskArn) + region := aws.RegionFromArn(*taskArn) cluster, taskID := SplitClusterTask(taskArn) return getTaskStatus(ctx, region, cluster, taskID) } diff --git a/src/pkg/clouds/aws/ecs/tail.go b/src/pkg/clouds/aws/ecs/tail.go index 40b7f96de..128010d2e 100644 --- a/src/pkg/clouds/aws/ecs/tail.go +++ b/src/pkg/clouds/aws/ecs/tail.go @@ -8,7 +8,7 @@ import ( "time" "github.com/DefangLabs/defang/src/pkg" - "github.com/DefangLabs/defang/src/pkg/clouds/aws/region" + "github.com/DefangLabs/defang/src/pkg/clouds/aws" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" ) @@ -16,7 +16,7 @@ const AwsLogsStreamPrefix = CrunProjectName func (a *AwsEcs) Tail(ctx context.Context, taskArn TaskArn) error { taskId := GetTaskID(taskArn) - a.Region = region.FromArn(*taskArn) + a.Region = aws.RegionFromArn(*taskArn) es, err := a.TailTaskID(ctx, taskId) if err != nil { return err diff --git a/src/pkg/clouds/aws/region.go b/src/pkg/clouds/aws/region.go new file mode 100644 index 000000000..cf14a541a --- /dev/null +++ b/src/pkg/clouds/aws/region.go @@ -0,0 +1,53 @@ +package aws + +import ( + "strings" +) + +type Region string + +func (r Region) String() string { + return string(r) +} + +const ( + RegionAFSouth1 Region = "af-south-1" + RegionAPEast1 Region = "ap-east-1" + RegionAPNortheast1 Region = "ap-northeast-1" + RegionAPNortheast2 Region = "ap-northeast-2" + RegionAPNortheast3 Region = "ap-northeast-3" + RegionAPSouth1 Region = "ap-south-1" + RegionAPSouth2 Region = "ap-south-2" + RegionAPSoutheast1 Region = "ap-southeast-1" + RegionAPSoutheast2 Region = "ap-southeast-2" + RegionAPSoutheast3 Region = "ap-southeast-3" + RegionAPSoutheast4 Region = "ap-southeast-4" + RegionCACentral Region = "ca-central-1" + RegionCNNorth1 Region = "cn-north-1" + RegionCNNorthwest1 Region = "cn-northwest-1" + RegionEUCentral1 Region = "eu-central-1" + RegionEUCentral2 Region = "eu-central-2" + RegionEUNorth1 Region = "eu-north-1" + RegionEUSouth1 Region = "eu-south-1" + RegionEUSouth2 Region = "eu-south-2" + RegionEUWest1 Region = "eu-west-1" + RegionEUWest2 Region = "eu-west-2" + RegionEUWest3 Region = "eu-west-3" + RegionMECentral1 Region = "me-central-1" + RegionMESouth1 Region = "me-south-1" + RegionSAEast1 Region = "sa-east-1" + RegionUSGovEast1 Region = "us-gov-east-1" + RegionUSGovWest1 Region = "us-gov-west-1" + RegionUSEast1 Region = "us-east-1" + RegionUSEast2 Region = "us-east-2" + RegionUSWest1 Region = "us-west-1" + RegionUSWest2 Region = "us-west-2" +) + +func RegionFromArn(arn string) Region { + parts := strings.Split(arn, ":") + if len(parts) < 6 || parts[0] != "arn" { + panic("invalid ARN") + } + return Region(parts[3]) +} diff --git a/src/pkg/clouds/aws/region/region.go b/src/pkg/clouds/aws/region/region.go deleted file mode 100644 index 9b3359cb0..000000000 --- a/src/pkg/clouds/aws/region/region.go +++ /dev/null @@ -1,51 +0,0 @@ -package region - -import ( - "strings" - - "github.com/DefangLabs/defang/src/pkg/clouds/aws" -) - -type Region = aws.Region - -const ( - AFSouth1 Region = "af-south-1" - APEast1 Region = "ap-east-1" - APNortheast1 Region = "ap-northeast-1" - APNortheast2 Region = "ap-northeast-2" - APNortheast3 Region = "ap-northeast-3" - APSouth1 Region = "ap-south-1" - APSouth2 Region = "ap-south-2" - APSoutheast1 Region = "ap-southeast-1" - APSoutheast2 Region = "ap-southeast-2" - APSoutheast3 Region = "ap-southeast-3" - APSoutheast4 Region = "ap-southeast-4" - CACentral Region = "ca-central-1" - CNNorth1 Region = "cn-north-1" - CNNorthwest1 Region = "cn-northwest-1" - EUCentral1 Region = "eu-central-1" - EUCentral2 Region = "eu-central-2" - EUNorth1 Region = "eu-north-1" - EUSouth1 Region = "eu-south-1" - EUSouth2 Region = "eu-south-2" - EUWest1 Region = "eu-west-1" - EUWest2 Region = "eu-west-2" - EUWest3 Region = "eu-west-3" - MECentral1 Region = "me-central-1" - MESouth1 Region = "me-south-1" - SAEast1 Region = "sa-east-1" - USGovEast1 Region = "us-gov-east-1" - USGovWest1 Region = "us-gov-west-1" - USEast1 Region = "us-east-1" - USEast2 Region = "us-east-2" - USWest1 Region = "us-west-1" - USWest2 Region = "us-west-2" -) - -func FromArn(arn string) Region { - parts := strings.Split(arn, ":") - if len(parts) < 6 || parts[0] != "arn" { - panic("invalid ARN") - } - return Region(parts[3]) -} diff --git a/src/pkg/clouds/azure/aci/common.go b/src/pkg/clouds/azure/aci/common.go new file mode 100644 index 000000000..642a5d2a9 --- /dev/null +++ b/src/pkg/clouds/azure/aci/common.go @@ -0,0 +1,80 @@ +package aci + +import ( + "errors" + "fmt" + "os" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2" + "github.com/DefangLabs/defang/src/pkg/clouds/azure" +) + +type ContainerInstance struct { + azure.Azure + containerGroupProps *armcontainerinstance.ContainerGroupPropertiesProperties + resourceGroupName string +} + +func NewContainerInstance(resourceGroupName string, location azure.Location) *ContainerInstance { + if location == "" { + location = azure.Location(os.Getenv("AZURE_LOCATION")) + } + return &ContainerInstance{ + Azure: azure.Azure{Location: location}, + resourceGroupName: resourceGroupName, // TODO: append location? + } +} +func newCreds() (string, *azidentity.DefaultAzureCredential, error) { + subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID") + if len(subscriptionID) == 0 { + return "", nil, errors.New("environment variable AZURE_SUBSCRIPTION_ID is not set") + } + + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return "", nil, fmt.Errorf("failed to create default Azure credentials: %w", err) + } + + return subscriptionID, cred, nil +} + +func newContainerGroupClient() (*armcontainerinstance.ContainerGroupsClient, error) { + subscriptionID, cred, err := newCreds() + if err != nil { + return nil, err + } + + clientFactory, err := armcontainerinstance.NewClientFactory(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create container group client: %w", err) + } + return clientFactory.NewContainerGroupsClient(), nil +} + +func newContainerClient() (*armcontainerinstance.ContainersClient, error) { + subscriptionID, cred, err := newCreds() + if err != nil { + return nil, err + } + + clientFactory, err := armcontainerinstance.NewClientFactory(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create container client: %w", err) + } + return clientFactory.NewContainersClient(), nil +} + +func newResourceGroupClient() (*armresources.ResourceGroupsClient, error) { + subscriptionID, cred, err := newCreds() + if err != nil { + return nil, err + } + + resourcesClientFactory, err := armresources.NewClientFactory(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create resource group client: %w", err) + } + return resourcesClientFactory.NewResourceGroupsClient(), nil +} diff --git a/src/pkg/clouds/azure/aci/common_test.go b/src/pkg/clouds/azure/aci/common_test.go new file mode 100644 index 000000000..670d0bb9e --- /dev/null +++ b/src/pkg/clouds/azure/aci/common_test.go @@ -0,0 +1,19 @@ +package aci + +import ( + "testing" + + "github.com/google/uuid" +) + +func TestNewClient(t *testing.T) { + t.Setenv("AZURE_SUBSCRIPTION_ID", uuid.NewString()) + + client, err := newContainerGroupClient() + if err != nil { + t.Fatalf("Failed to create client: %v", err) + } + if client == nil { + t.Fatal("Expected non-nil client") + } +} diff --git a/src/pkg/clouds/azure/aci/run.go b/src/pkg/clouds/azure/aci/run.go new file mode 100644 index 000000000..998299477 --- /dev/null +++ b/src/pkg/clouds/azure/aci/run.go @@ -0,0 +1,69 @@ +package aci + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2" + "github.com/DefangLabs/defang/src/pkg" + "github.com/DefangLabs/defang/src/pkg/types" +) + +type ContainerGroupName = types.TaskID + +// safeAppend is like append but avoids mutating any aliases of the slice. +func safeAppend[T any](slice []T, elems ...T) []T { + return append(slice[:len(slice):len(slice)], elems...) +} + +func (c *ContainerInstance) Run(ctx context.Context, env map[string]string, args ...string) (ContainerGroupName, error) { + containerGroupClient, err := newContainerGroupClient() + if err != nil { + return nil, err + } + var props armcontainerinstance.ContainerProperties + for key, value := range env { + props.EnvironmentVariables = append(props.EnvironmentVariables, &armcontainerinstance.EnvironmentVariable{ + Name: to.Ptr(key), + Value: to.Ptr(value), + }) + } + for _, arg := range args { + props.Command = append(props.Command, to.Ptr(arg)) + } + + clone := *c.containerGroupProps + for i, container := range clone.Containers { + newProps := *container.Properties + newProps.Command = safeAppend(newProps.Command, props.Command...) + newProps.EnvironmentVariables = safeAppend(newProps.EnvironmentVariables, props.EnvironmentVariables...) + clone.Containers[i] = &armcontainerinstance.Container{ + Name: container.Name, + Properties: &newProps, + } + } + + groupName := containerGroupName + "-" + pkg.RandomID() + group := armcontainerinstance.ContainerGroup{ + Name: to.Ptr(groupName), + Location: c.Location.Ptr(), + Properties: &clone, + } + _, err = containerGroupClient.BeginCreateOrUpdate(ctx, c.resourceGroupName, groupName, group, nil) + if err != nil { + return nil, fmt.Errorf("failed to create container group: %w", err) + } + + _, err = containerGroupClient.BeginStart(ctx, c.resourceGroupName, groupName, nil) + if err != nil { + return nil, fmt.Errorf("failed to start container group: %w", err) + } + + // createResponse.Done() + // res, err := createResponse.PollUntilDone(ctx, nil) + // if err != nil { + // return nil, err + // } + return &groupName, nil +} diff --git a/src/pkg/clouds/azure/aci/run_test.go b/src/pkg/clouds/azure/aci/run_test.go new file mode 100644 index 000000000..c32d50839 --- /dev/null +++ b/src/pkg/clouds/azure/aci/run_test.go @@ -0,0 +1,43 @@ +//go:integration + +package aci + +import ( + "context" + "testing" + + "github.com/DefangLabs/defang/src/pkg/types" +) + +func TestRun(t *testing.T) { + t.SkipNow() // too slow for CI + + ctx := context.Background() + + containerInstance := NewContainerInstance(testResourceGroupName, "westeurope") + + err := containerInstance.SetUp(ctx, []types.Container{ + { + Name: "test-container", + Image: "library/alpine:latest", + }, + }) + if err != nil { + t.Fatalf("SetUp failed: %v", err) + } + + t.Cleanup(func() { + // err := containerInstance.TearDown(ctx) + // if err != nil { + // t.Fatalf("Failed to tear down container instance: %v", err) + // } + }) + + taskID, err := containerInstance.Run(ctx, nil) + if err != nil { + t.Fatalf("Run failed: %v", err) + } + if taskID == nil { + t.Fatal("Expected non-nil task ID") + } +} diff --git a/src/pkg/clouds/azure/aci/setup.go b/src/pkg/clouds/azure/aci/setup.go new file mode 100644 index 000000000..64bb2df0a --- /dev/null +++ b/src/pkg/clouds/azure/aci/setup.go @@ -0,0 +1,93 @@ +package aci + +import ( + "context" + "fmt" + "math" + "os" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2" + "github.com/DefangLabs/defang/src/pkg" + "github.com/DefangLabs/defang/src/pkg/types" +) + +const containerGroupName = "defang-cd" + +func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Container) error { + resourceGroupClient, err := newResourceGroupClient() + if err != nil { + return err + } + _, err = resourceGroupClient.CreateOrUpdate(ctx, c.resourceGroupName, armresources.ResourceGroup{ + Location: c.Location.Ptr(), + }, nil) + if err != nil { + return fmt.Errorf("failed to create resource group: %w", err) + } + + c.containerGroupProps = &armcontainerinstance.ContainerGroupPropertiesProperties{ + OSType: to.Ptr(armcontainerinstance.OperatingSystemTypesLinux), // TODO: from Platform + // Priority: to.Ptr(armcontainerinstance.ContainerGroupPrioritySpot), + RestartPolicy: to.Ptr(armcontainerinstance.ContainerGroupRestartPolicyNever), + } + if username := os.Getenv("DOCKERHUB_USERNAME"); username != "" { + c.containerGroupProps.ImageRegistryCredentials = append(c.containerGroupProps.ImageRegistryCredentials, &armcontainerinstance.ImageRegistryCredential{ + Server: to.Ptr("index.docker.io"), + Username: to.Ptr(username), + Password: to.Ptr(pkg.Getenv("DOCKERHUB_TOKEN", os.Getenv("DOCKERHUB_PASSWORD"))), + }) + } + + for _, container := range containers { + cpus := math.Max(0.01, container.Cpus) // ensure minimum CPU is 0.01 + memoryInGB := math.Max(0.1, float64(container.Memory)/1024.0/1024.0/1024.0) // convert from B to GB + properties := &armcontainerinstance.ContainerProperties{ + Image: to.Ptr(container.Image), + Resources: &armcontainerinstance.ResourceRequirements{ + Requests: &armcontainerinstance.ResourceRequests{ + CPU: to.Ptr(math.Round(100*cpus) / 100), // round to 2 decimal places + MemoryInGB: to.Ptr(math.Round(10*memoryInGB) * 0.1), // Round to 1 decimal place + }, + }, + } + for _, command := range container.Command { + properties.Command = append(properties.Command, to.Ptr(command)) + } + c.containerGroupProps.Containers = append(c.containerGroupProps.Containers, &armcontainerinstance.Container{ + Name: to.Ptr(container.Name), + Properties: properties, + }) + } + + // newContainerGroupClient, err := newContainerGroupClient() + // if err != nil { + // return err + // } + // deleteResponse, err := newContainerGroupClient.BeginDelete(ctx, c.resourceGroupName, containerGroupName, nil) + // if err != nil { + // return err + // } + // _, err = deleteResponse.PollUntilDone(ctx, nil) + // if err != nil { + // return err + // } + return nil +} + +func (c *ContainerInstance) TearDown(ctx context.Context) error { + resourceGroupClient, err := newResourceGroupClient() + if err != nil { + return err + } + deleteResponse, err := resourceGroupClient.BeginDelete(ctx, c.resourceGroupName, nil) + if err != nil { + return fmt.Errorf("failed to delete resource group: %w", err) + } + _, err = deleteResponse.PollUntilDone(ctx, nil) + if err != nil { + return err + } + return nil +} diff --git a/src/pkg/clouds/azure/aci/setup_test.go b/src/pkg/clouds/azure/aci/setup_test.go new file mode 100644 index 000000000..7eef79fad --- /dev/null +++ b/src/pkg/clouds/azure/aci/setup_test.go @@ -0,0 +1,42 @@ +//go:integration + +package aci + +import ( + "context" + "testing" + + "github.com/DefangLabs/defang/src/pkg" + "github.com/DefangLabs/defang/src/pkg/types" +) + +var testResourceGroupName = "crun-test-" + pkg.GetCurrentUser() // avoid conflict with other users in the same account + +func TestSetup(t *testing.T) { + if testing.Short() { + t.Skip("skipping slow integration test in short mode") + } + + c := NewContainerInstance(testResourceGroupName, "westeurope") + + t.Run("SetUp", func(t *testing.T) { + err := c.SetUp(context.Background(), []types.Container{ + { + Name: "test-container", + Image: "library/nginx:latest", + Cpus: 1, + Memory: 1024 * 1024 * 1024, // 1 GB in B + }, + }) + if err != nil { + t.Errorf("Failed to set up container instance: %v", err) + } + }) + + t.Run("TearDown", func(t *testing.T) { + err := c.TearDown(context.Background()) + if err != nil { + t.Fatalf("Failed to tear down container instance: %v", err) + } + }) +} diff --git a/src/pkg/clouds/azure/aci/stop.go b/src/pkg/clouds/azure/aci/stop.go new file mode 100644 index 000000000..3c82929a8 --- /dev/null +++ b/src/pkg/clouds/azure/aci/stop.go @@ -0,0 +1,18 @@ +package aci + +import ( + "context" +) + +func (c *ContainerInstance) Stop(ctx context.Context, groupName ContainerGroupName) error { + containerGroupClient, err := newContainerGroupClient() + if err != nil { + return err + } + + _, err = containerGroupClient.Stop(ctx, c.resourceGroupName, *groupName, nil) + if err != nil { + return err + } + return nil +} diff --git a/src/pkg/clouds/azure/aci/tail.go b/src/pkg/clouds/azure/aci/tail.go new file mode 100644 index 000000000..26816fbdb --- /dev/null +++ b/src/pkg/clouds/azure/aci/tail.go @@ -0,0 +1,127 @@ +package aci + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "os" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2" + "github.com/gorilla/websocket" +) + +type logEntry struct { + Message string + Stderr bool + Err error +} + +func (c *ContainerInstance) Tail(ctx context.Context, groupName ContainerGroupName) error { + container := c.containerGroupProps.Containers[0] + + ch, err := c.StreamLogs(ctx, groupName, *container.Name) + if err != nil { + return err + } + + for entry := range ch { + if entry.Err != nil { + return entry.Err + } + if entry.Stderr { + fmt.Fprint(os.Stderr, entry.Message) + } else { + fmt.Print(entry.Message) + } + } + return io.EOF +} + +func (c *ContainerInstance) QueryLogs(ctx context.Context, groupName ContainerGroupName, containerName string) (string, error) { + client, err := newContainerClient() + if err != nil { + return "", err + } + + for { + logResponse, err := client.ListLogs(ctx, c.resourceGroupName, *groupName, containerName, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.ErrorCode == "ContainerGroupDeploymentNotReady" { + time.Sleep(time.Second) // Wait before retrying + continue // Retry if the deployment is not ready yet + } + return "", fmt.Errorf("failed to list logs: %w", err) + } + if logResponse.Logs.Content == nil { + return "", io.EOF + } + return *logResponse.Logs.Content, nil + } +} + +func (c *ContainerInstance) StreamLogs(ctx context.Context, groupName ContainerGroupName, containerName string) (<-chan logEntry, error) { + client, err := newContainerClient() + if err != nil { + return nil, err + } + + var attachResponse armcontainerinstance.ContainersClientAttachResponse + for { + attachResponse, err = client.Attach(ctx, c.resourceGroupName, *groupName, containerName, nil) + if err != nil { + var respErr *azcore.ResponseError + if errors.As(err, &respErr) && respErr.ErrorCode == "ContainerNotFound" { + time.Sleep(time.Second) // Wait before retrying + continue // Retry if the container is not found yet + } + return nil, fmt.Errorf("failed to attach to container: %w", err) + } + break + } + + header := http.Header{} + header.Set("Authorization", *attachResponse.Password) + conn, resp, err := websocket.DefaultDialer.DialContext(ctx, *attachResponse.WebSocketURI, header) + defer resp.Body.Close() + if err != nil { + return nil, fmt.Errorf("failed to connect to websocket (%s): %w", resp.Status, err) + } + + ctx, cancel := context.WithCancel(ctx) + + go func() { + <-ctx.Done() + _ = conn.Close() // unblock conn.ReadMessage + }() + + ch := make(chan logEntry) + go func() { + defer close(ch) + defer cancel() + + for { + _, logLine, err := conn.ReadMessage() + if err != nil { + if !websocket.IsCloseError(err, websocket.CloseNormalClosure) { + select { + case ch <- logEntry{Err: err}: + case <-ctx.Done(): + } + } + return + } + stdioFd := logLine[0] + select { + case ch <- logEntry{Message: string(logLine[1:]), Stderr: stdioFd == 2}: + case <-ctx.Done(): + return + } + } + }() + return ch, nil +} diff --git a/src/pkg/clouds/azure/aci/tail_test.go b/src/pkg/clouds/azure/aci/tail_test.go new file mode 100644 index 000000000..cfd285282 --- /dev/null +++ b/src/pkg/clouds/azure/aci/tail_test.go @@ -0,0 +1,57 @@ +//go:integration + +package aci + +import ( + "context" + "io" + "testing" + + "github.com/DefangLabs/defang/src/pkg/types" +) + +func TestTail(t *testing.T) { + t.SkipNow() // too slow for CI + + ctx := context.Background() + + containerInstance := NewContainerInstance(testResourceGroupName, "westeurope") + + err := containerInstance.SetUp(ctx, []types.Container{ + { + Name: "test-container", + Image: "library/alpine:latest", + Command: []string{"sh", "-c", "sleep 3; cat /etc/hosts"}, + }, + }) + if err != nil { + t.Fatalf("SetUp failed: %v", err) + } + + t.Cleanup(func() { + // err := containerInstance.TearDown(ctx) + // if err != nil { + // t.Fatalf("Failed to tear down container instance: %v", err) + // } + }) + + taskID, err := containerInstance.Run(ctx, nil) + if err != nil { + t.Fatalf("Run failed: %v", err) + } + if taskID == nil { + t.Fatal("Expected non-nil task ID") + } + + t.Cleanup(func() { + err := containerInstance.Stop(ctx, taskID) + if err != nil { + t.Fatalf("Failed to stop container instance: %v", err) + } + }) + + err = containerInstance.Tail(ctx, taskID) + if err != io.EOF { + t.Fatalf("Tail failed: %v", err) + } +} diff --git a/src/pkg/clouds/azure/common.go b/src/pkg/clouds/azure/common.go new file mode 100644 index 000000000..ca11cc393 --- /dev/null +++ b/src/pkg/clouds/azure/common.go @@ -0,0 +1,5 @@ +package azure + +type Azure struct { + Location Location +} diff --git a/src/pkg/clouds/azure/job.go b/src/pkg/clouds/azure/job.go new file mode 100644 index 000000000..28ea4f9cc --- /dev/null +++ b/src/pkg/clouds/azure/job.go @@ -0,0 +1,32 @@ +package azure + +import ( + "context" + + "github.com/DefangLabs/defang/src/pkg/types" +) + +type ContainerJob struct { +} + +// var _ types.Driver = (*ContainerJob)(nil) + +// CreateUploadURL implements types.Driver. +func (c *ContainerJob) CreateUploadURL(ctx context.Context, name string) (string, error) { + panic("unimplemented") +} + +// GetInfo implements types.Driver. +func (c *ContainerJob) GetInfo(ctx context.Context, taskID types.TaskID) (*types.TaskInfo, error) { + panic("unimplemented") +} + +// ListSecrets implements types.Driver. +func (c *ContainerJob) ListSecrets(ctx context.Context) ([]string, error) { + panic("unimplemented") +} + +// PutSecret implements types.Driver. +func (c *ContainerJob) PutSecret(ctx context.Context, name string, value string) error { + panic("unimplemented") +} diff --git a/src/pkg/clouds/azure/location.go b/src/pkg/clouds/azure/location.go new file mode 100644 index 000000000..d1023e243 --- /dev/null +++ b/src/pkg/clouds/azure/location.go @@ -0,0 +1,120 @@ +package azure + +type Location string + +const ( + LocationAsia Location = "asia" + LocationAsiaPacific Location = "asiapacific" + LocationAustralia Location = "australia" + LocationAustraliaCentral Location = "australiacentral" + LocationAustraliaCentral2 Location = "australiacentral2" + LocationAustraliaEast Location = "australiaeast" + LocationAustraliaSouthEast Location = "australiasoutheast" + LocationAustriaEast Location = "austriaeast" + LocationBrazil Location = "brazil" + LocationBrazilSouth Location = "brazilsouth" + LocationBrazilSouthEast Location = "brazilsoutheast" + LocationBrazilUS Location = "brazilus" + LocationCanada Location = "canada" + LocationCanadaCentral Location = "canadacentral" + LocationCanadaEast Location = "canadaeast" + LocationCentralIndia Location = "centralindia" + LocationCentralUS Location = "centralus" + LocationCentralUSEuap Location = "centraluseuap" + LocationCentralUSStage Location = "centralusstage" + LocationChileCentral Location = "chilecentral" + LocationEastAsia Location = "eastasia" + LocationEastAsiaStage Location = "eastasiastage" + LocationEastUS Location = "eastus" + LocationEastUS2 Location = "eastus2" + LocationEastUS2Euap Location = "eastus2euap" + LocationEastUS2Stage Location = "eastus2stage" + LocationEastUSStage Location = "eastusstage" + LocationEastUSStg Location = "eastusstg" + LocationEurope Location = "europe" + LocationFrance Location = "france" + LocationFranceCentral Location = "francecentral" + LocationFranceSouth Location = "francesouth" + LocationGermany Location = "germany" + LocationGermanyNorth Location = "germanynorth" + LocationGermanyWestCentral Location = "germanywestcentral" + LocationGlobal Location = "global" + LocationIndia Location = "india" + LocationIndonesia Location = "indonesia" + LocationIndonesiaCentral Location = "indonesiacentral" + LocationIsrael Location = "israel" + LocationIsraelCentral Location = "israelcentral" + LocationItaly Location = "italy" + LocationItalyNorth Location = "italynorth" + LocationJapan Location = "japan" + LocationJapanEast Location = "japaneast" + LocationJapanWest Location = "japanwest" + LocationJioIndiaCentral Location = "jioindiacentral" + LocationJioIndiaWest Location = "jioindiawest" + LocationKorea Location = "korea" + LocationKoreaCentral Location = "koreacentral" + LocationKoreaSouth Location = "koreasouth" + LocationMalaysia Location = "malaysia" + LocationMalaysiaWest Location = "malaysiawest" + LocationMexico Location = "mexico" + LocationMexicoCentral Location = "mexicocentral" + LocationNewZealand Location = "newzealand" + LocationNewZealandNorth Location = "newzealandnorth" + LocationNorthCentralUS Location = "northcentralus" + LocationNorthCentralUSStage Location = "northcentralusstage" + LocationNorthEurope Location = "northeurope" + LocationNorway Location = "norway" + LocationNorwayEast Location = "norwayeast" + LocationNorwayWest Location = "norwaywest" + LocationPoland Location = "poland" + LocationPolandCentral Location = "polandcentral" + LocationQatar Location = "qatar" + LocationQatarCentral Location = "qatarcentral" + LocationSingapore Location = "singapore" + LocationSouthAfrica Location = "southafrica" + LocationSouthAfricaNorth Location = "southafricanorth" + LocationSouthAfricaWest Location = "southafricawest" + LocationSouthCentralUS Location = "southcentralus" + LocationSouthCentralUSStage Location = "southcentralusstage" + LocationSouthCentralUSStg Location = "southcentralusstg" + LocationSoutheastAsia Location = "southeastasia" + LocationSoutheastAsiaStage Location = "southeastasiastage" + LocationSouthIndia Location = "southindia" + LocationSpain Location = "spain" + LocationSpainCentral Location = "spaincentral" + LocationSweden Location = "sweden" + LocationSwedenCentral Location = "swedencentral" + LocationSwedenSouth Location = "swedensouth" + LocationSwitzerland Location = "switzerland" + LocationSwitzerlandNorth Location = "switzerlandnorth" + LocationSwitzerlandWest Location = "switzerlandwest" + LocationTaiwan Location = "taiwan" + LocationUae Location = "uae" + LocationUaeCentral Location = "uaecentral" + LocationUaeNorth Location = "uaenorth" + LocationUK Location = "uk" + LocationUKSouth Location = "uksouth" + LocationUKWest Location = "ukwest" + LocationUnitedStates Location = "unitedstates" + LocationUnitedStatesEuap Location = "unitedstateseuap" + LocationWestCentralUS Location = "westcentralus" + LocationWestEurope Location = "westeurope" + LocationWestIndia Location = "westindia" + LocationWestUS Location = "westus" + LocationWestUS2 Location = "westus2" + LocationWestUS2Stage Location = "westus2stage" + LocationWestUS3 Location = "westus3" + LocationWestUSStage Location = "westusstage" +) + +func (l Location) String() string { + return string(l) +} + +func (l Location) Ptr() *string { + if l == "" { + return nil + } + s := string(l) + return &s +} diff --git a/src/pkg/clouds/gcp/cloudrun.go b/src/pkg/clouds/gcp/cloudrun.go index 1df57cd88..aad14329b 100644 --- a/src/pkg/clouds/gcp/cloudrun.go +++ b/src/pkg/clouds/gcp/cloudrun.go @@ -182,10 +182,10 @@ func (gcp Gcp) GetExecutionEnv(ctx context.Context, executionName string) (map[s } // FIXME: Add tests -func FixupGcpConfig(vCpu float32, memoryMiB uint64) (cpu float64, memory uint) { +func FixupGcpConfig(vCpu float64, memoryMiB uint64) (cpu float64, memory uint) { // Fixup CPU value and minimum memory according to // https://cloud.google.com/run/docs/configuring/jobs/cpu - cpu = math.Trunc(float64(vCpu)*100) / 100 // Cpu value below 1 should be in increments of 0.01 + cpu = math.Trunc(vCpu*100) / 100 // Cpu value below 1 should be in increments of 0.01 if cpu > 1 { cpu = math.Ceil(float64(vCpu)) // Any value above 1 must be an integer value } diff --git a/src/pkg/cmd/factory.go b/src/pkg/cmd/factory.go index 095cee2c0..ad8f4c603 100644 --- a/src/pkg/cmd/factory.go +++ b/src/pkg/cmd/factory.go @@ -4,51 +4,51 @@ import ( "fmt" "github.com/DefangLabs/defang/src/pkg" + "github.com/DefangLabs/defang/src/pkg/clouds/aws" "github.com/DefangLabs/defang/src/pkg/clouds/aws/ecs/cfn" - "github.com/DefangLabs/defang/src/pkg/clouds/aws/region" "github.com/DefangLabs/defang/src/pkg/docker" "github.com/DefangLabs/defang/src/pkg/types" ) type DriverOption func(types.Driver) error -func createDriver(reg Region, opts ...DriverOption) (types.Driver, error) { +func createDriver(reg aws.Region, opts ...DriverOption) (types.Driver, error) { var driver types.Driver switch reg { case "docker", "local", "": driver = docker.New() case - region.AFSouth1, // "af-south-1" - region.APEast1, // "ap-east-1" - region.APNortheast1, // "ap-northeast-1" - region.APNortheast2, // "ap-northeast-2" - region.APNortheast3, // "ap-northeast-3" - region.APSouth1, // "ap-south-1" - region.APSouth2, // "ap-south-2" - region.APSoutheast1, // "ap-southeast-1" - region.APSoutheast2, // "ap-southeast-2" - region.APSoutheast3, // "ap-southeast-3" - region.APSoutheast4, // "ap-southeast-4" - region.CACentral, // "ca-central-1" - region.CNNorth1, // "cn-north-1" - region.CNNorthwest1, // "cn-northwest-1" - region.EUCentral1, // "eu-central-1" - region.EUCentral2, // "eu-central-2" - region.EUNorth1, // "eu-north-1" - region.EUSouth1, // "eu-south-1" - region.EUSouth2, // "eu-south-2" - region.EUWest1, // "eu-west-1" - region.EUWest2, // "eu-west-2" - region.EUWest3, // "eu-west-3" - region.MECentral1, // "me-central-1" - region.MESouth1, // "me-south-1" - region.SAEast1, // "sa-east-1" - region.USGovEast1, // "us-gov-east-1" - region.USGovWest1, // "us-gov-west-1" - region.USEast1, // "us-east-1" - region.USEast2, // "us-east-2" - region.USWest1, // "us-west-1" - region.USWest2: // "us-west-2" + aws.RegionAFSouth1, // "af-south-1" + aws.RegionAPEast1, // "ap-east-1" + aws.RegionAPNortheast1, // "ap-northeast-1" + aws.RegionAPNortheast2, // "ap-northeast-2" + aws.RegionAPNortheast3, // "ap-northeast-3" + aws.RegionAPSouth1, // "ap-south-1" + aws.RegionAPSouth2, // "ap-south-2" + aws.RegionAPSoutheast1, // "ap-southeast-1" + aws.RegionAPSoutheast2, // "ap-southeast-2" + aws.RegionAPSoutheast3, // "ap-southeast-3" + aws.RegionAPSoutheast4, // "ap-southeast-4" + aws.RegionCACentral, // "ca-central-1" + aws.RegionCNNorth1, // "cn-north-1" + aws.RegionCNNorthwest1, // "cn-northwest-1" + aws.RegionEUCentral1, // "eu-central-1" + aws.RegionEUCentral2, // "eu-central-2" + aws.RegionEUNorth1, // "eu-north-1" + aws.RegionEUSouth1, // "eu-south-1" + aws.RegionEUSouth2, // "eu-south-2" + aws.RegionEUWest1, // "eu-west-1" + aws.RegionEUWest2, // "eu-west-2" + aws.RegionEUWest3, // "eu-west-3" + aws.RegionMECentral1, // "me-central-1" + aws.RegionMESouth1, // "me-south-1" + aws.RegionSAEast1, // "sa-east-1" + aws.RegionUSGovEast1, // "us-gov-east-1" + aws.RegionUSGovWest1, // "us-gov-west-1" + aws.RegionUSEast1, // "us-east-1" + aws.RegionUSEast2, // "us-east-2" + aws.RegionUSWest1, // "us-west-1" + aws.RegionUSWest2: // "us-west-2" driver = cfn.New(stackName(pkg.GetCurrentUser()), reg) default: return nil, fmt.Errorf("unsupported region: %v", reg) diff --git a/src/pkg/types/driver.go b/src/pkg/types/driver.go index 20947d393..7e3ff1a67 100644 --- a/src/pkg/types/driver.go +++ b/src/pkg/types/driver.go @@ -9,28 +9,20 @@ const ( ) type TaskID *string -type ContainerCondition string - -const ( - ContainerStarted ContainerCondition = "START" - ContainerComplete = "COMPLETE" - ContainerSuccess = "SUCCESS" - ContainerHealthy = "HEALTHY" -) type Container struct { - Image string - Name string - Cpus float32 - Memory uint64 - Platform string - Essential *bool // default true + Image string + Name string + Cpus float64 + Memory uint64 + Platform string + IsInit bool // whether this container is an init container (non-essential) + Volumes []TaskVolume - VolumesFrom []string // container (default rw), container:rw, or container:ro + VolumesFrom []string // "container" (default rw), "container:rw", or "container:ro" EntryPoint []string Command []string // overridden by Run() - WorkDir string - DependsOn map[string]ContainerCondition // container name -> condition + WorkDir string // Deprecated: not supported by ACI } type TaskVolume struct { diff --git a/src/protos/io/defang/v1/fabric.pb.go b/src/protos/io/defang/v1/fabric.pb.go index a77f6ad74..3dd34648b 100644 --- a/src/protos/io/defang/v1/fabric.pb.go +++ b/src/protos/io/defang/v1/fabric.pb.go @@ -33,6 +33,7 @@ const ( Provider_AWS Provider = 2 Provider_DIGITALOCEAN Provider = 3 Provider_GCP Provider = 4 + Provider_AZURE Provider = 5 ) // Enum value maps for Provider. @@ -43,6 +44,7 @@ var ( 2: "AWS", 3: "DIGITALOCEAN", 4: "GCP", + 5: "AZURE", } Provider_value = map[string]int32{ "PROVIDER_UNSPECIFIED": 0, @@ -50,6 +52,7 @@ var ( "AWS": 2, "DIGITALOCEAN": 3, "GCP": 4, + "AZURE": 5, } ) @@ -3559,7 +3562,7 @@ type ProjectUpdate struct { Mode DeploymentMode `protobuf:"varint,6,opt,name=mode,proto3,enum=io.defang.v1.DeploymentMode" json:"mode,omitempty"` Provider Provider `protobuf:"varint,7,opt,name=provider,proto3,enum=io.defang.v1.Provider" json:"provider,omitempty"` ProjectOutputsVersion uint32 `protobuf:"varint,8,opt,name=project_outputs_version,json=projectOutputsVersion,proto3" json:"project_outputs_version,omitempty"` - ProjectOutputs []byte `protobuf:"bytes,9,opt,name=project_outputs,json=projectOutputs,proto3" json:"project_outputs,omitempty"` // JSON serialization of pulumi outputs. schema versioned using project_outputs_version + ProjectOutputs []byte `protobuf:"bytes,9,opt,name=project_outputs,json=projectOutputs,proto3" json:"project_outputs,omitempty"` // JSON serialization of pulumi outputs. schema unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -6274,295 +6277,296 @@ var file_io_defang_v1_fabric_proto_rawDesc = []byte{ 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x2a, 0x54, 0x0a, 0x08, 0x50, 0x72, 0x6f, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x2a, 0x5f, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x46, 0x41, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x49, 0x47, 0x49, 0x54, 0x41, 0x4c, 0x4f, - 0x43, 0x45, 0x41, 0x4e, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x04, 0x2a, - 0x54, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, - 0x65, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x45, 0x56, 0x45, 0x4c, - 0x4f, 0x50, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x47, - 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, - 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xa3, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x55, 0x49, - 0x4c, 0x44, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x42, - 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x49, 0x4e, - 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x50, 0x45, 0x4e, - 0x44, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, - 0x41, 0x43, 0x54, 0x49, 0x56, 0x41, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x11, 0x0a, 0x0d, - 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, - 0x12, 0x0a, 0x0e, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, - 0x47, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x51, 0x55, - 0x45, 0x55, 0x45, 0x44, 0x10, 0x07, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x08, 0x12, 0x18, - 0x0a, 0x14, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x4f, 0x4d, - 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x45, 0x50, 0x4c, - 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x0a, 0x12, - 0x10, 0x0a, 0x0c, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, - 0x0b, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, - 0x53, 0x43, 0x41, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x10, 0x0c, 0x2a, 0x42, 0x0a, 0x0a, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, - 0x46, 0x49, 0x47, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x4e, 0x53, 0x49, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x2a, - 0x6a, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, - 0x1a, 0x0a, 0x16, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x2a, 0x6b, 0x0a, 0x10, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, - 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x2a, 0x3f, 0x0a, 0x08, 0x50, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, 0x41, 0x4d, - 0x44, 0x36, 0x34, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, 0x41, - 0x52, 0x4d, 0x36, 0x34, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, - 0x41, 0x4e, 0x59, 0x10, 0x02, 0x1a, 0x02, 0x18, 0x01, 0x2a, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x07, - 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, - 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, - 0x54, 0x50, 0x32, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, 0x05, 0x1a, - 0x02, 0x18, 0x01, 0x2a, 0x21, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x48, - 0x4f, 0x53, 0x54, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, - 0x10, 0x01, 0x1a, 0x02, 0x18, 0x01, 0x2a, 0x37, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, - 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x1a, 0x02, 0x18, 0x01, 0x2a, - 0x61, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, - 0x69, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x55, 0x42, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x4f, 0x42, 0x42, 0x59, 0x10, - 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x12, - 0x07, 0x0a, 0x03, 0x50, 0x52, 0x4f, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x41, 0x4d, - 0x10, 0x04, 0x32, 0x8b, 0x1a, 0x0a, 0x10, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x40, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, - 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x40, 0x0a, 0x05, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x12, 0x1a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x52, - 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x04, 0x54, 0x61, - 0x69, 0x6c, 0x12, 0x19, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, + 0x43, 0x45, 0x41, 0x4e, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x04, 0x12, + 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x10, 0x05, 0x2a, 0x54, 0x0a, 0x0e, 0x44, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x10, + 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x4f, 0x50, 0x4d, 0x45, 0x4e, + 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x47, 0x49, 0x4e, 0x47, 0x10, 0x02, + 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, + 0x2a, 0xa3, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x51, 0x55, + 0x45, 0x55, 0x45, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, + 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x11, + 0x0a, 0x0d, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, + 0x03, 0x12, 0x14, 0x0a, 0x10, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x41, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x11, 0x0a, 0x0d, 0x42, 0x55, 0x49, 0x4c, 0x44, + 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x55, + 0x49, 0x4c, 0x44, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x11, + 0x0a, 0x0d, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x44, 0x10, + 0x07, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, + 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x50, + 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, + 0x44, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x55, + 0x49, 0x4c, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x0b, 0x12, 0x18, 0x0a, 0x14, + 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x43, 0x41, 0x4c, 0x45, + 0x44, 0x5f, 0x49, 0x4e, 0x10, 0x0c, 0x2a, 0x42, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, + 0x45, 0x4e, 0x53, 0x49, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x2a, 0x6a, 0x0a, 0x0e, 0x44, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, + 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, + 0x17, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x44, 0x45, + 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x2a, 0x6b, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, + 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, + 0x14, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x44, 0x45, 0x50, 0x4c, 0x4f, + 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x4f, 0x57, + 0x4e, 0x10, 0x02, 0x2a, 0x3f, 0x0a, 0x08, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, + 0x0f, 0x0a, 0x0b, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, 0x41, 0x4d, 0x44, 0x36, 0x34, 0x10, 0x00, + 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, + 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, 0x41, 0x4e, 0x59, 0x10, 0x02, + 0x1a, 0x02, 0x18, 0x01, 0x2a, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, + 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x48, + 0x54, 0x54, 0x50, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x32, 0x10, 0x04, + 0x12, 0x08, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x10, 0x05, 0x1a, 0x02, 0x18, 0x01, 0x2a, 0x21, + 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x00, + 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x1a, 0x02, 0x18, + 0x01, 0x2a, 0x37, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0f, 0x0a, 0x0b, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, + 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x1a, 0x02, 0x18, 0x01, 0x2a, 0x61, 0x0a, 0x10, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x65, 0x72, 0x12, 0x21, + 0x0a, 0x1d, 0x53, 0x55, 0x42, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, + 0x49, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x4f, 0x42, 0x42, 0x59, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, + 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x52, + 0x4f, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x04, 0x32, 0x8b, 0x1a, + 0x0a, 0x10, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x90, + 0x02, 0x01, 0x12, 0x40, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, + 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x03, 0x90, 0x02, 0x01, 0x12, 0x40, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x2e, + 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, + 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x04, 0x54, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x3f, 0x0a, 0x06, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, - 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x43, 0x0a, 0x06, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x12, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, - 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3f, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, - 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x03, 0x90, - 0x02, 0x01, 0x12, 0x48, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, - 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x4b, 0x0a, 0x07, - 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x12, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, - 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, - 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x44, 0x0a, 0x07, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, - 0x4e, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1e, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, - 0x57, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x20, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, + 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x3f, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x15, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x43, 0x0a, 0x06, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x12, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x03, + 0x47, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x48, 0x0a, + 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x4b, 0x0a, 0x07, 0x44, 0x65, 0x73, 0x74, 0x72, + 0x6f, 0x79, 0x12, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x73, 0x74, 0x72, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x90, 0x02, 0x02, 0x12, 0x44, 0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, + 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x4e, 0x0a, 0x09, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x64, + 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x69, 0x6f, + 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, + 0x90, 0x02, 0x01, 0x12, 0x58, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, + 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, + 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x21, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x58, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, - 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, - 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x40, 0x0a, - 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x1a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, - 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3a, 0x0a, 0x08, 0x53, 0x69, 0x67, 0x6e, 0x45, 0x55, 0x4c, 0x41, 0x12, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x08, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x54, 0x6f, 0x53, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x48, 0x0a, 0x09, - 0x50, 0x75, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x40, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x12, 0x1a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x69, + 0x67, 0x6e, 0x45, 0x55, 0x4c, 0x41, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x08, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, + 0x6f, 0x53, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x48, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x88, 0x02, + 0x01, 0x12, 0x43, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x12, 0x15, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x4e, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x06, + 0x88, 0x02, 0x01, 0x90, 0x02, 0x01, 0x12, 0x54, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x48, 0x0a, 0x09, + 0x50, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x43, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x15, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, - 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x4e, 0x0a, 0x0b, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x69, 0x6f, 0x2e, - 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x22, 0x06, 0x88, 0x02, 0x01, 0x90, 0x02, 0x01, 0x12, 0x54, 0x0a, 0x0a, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x64, - 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x69, 0x6f, 0x2e, - 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x50, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x57, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x20, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x69, 0x6f, 0x2e, 0x64, + 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, - 0x01, 0x12, 0x48, 0x0a, 0x09, 0x50, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, - 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x50, 0x0a, 0x0d, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x22, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x57, 0x0a, - 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x20, 0x2e, 0x69, + 0x01, 0x12, 0x50, 0x0a, 0x0d, 0x50, 0x75, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x75, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, + 0x90, 0x02, 0x02, 0x12, 0x63, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, + 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, - 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x50, 0x0a, 0x0d, 0x50, 0x75, 0x74, 0x44, 0x65, 0x70, - 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, - 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x63, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, - 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x69, 0x6f, - 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x25, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x52, 0x0a, - 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, - 0x12, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x70, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x2a, 0x2e, 0x69, 0x6f, 0x2e, - 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, - 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, - 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x28, 0x2e, 0x69, 0x6f, 0x2e, - 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, - 0x02, 0x12, 0x7b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x2d, 0x2e, - 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x52, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x69, 0x6f, + 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x69, 0x6f, + 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x4a, - 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x43, 0x0a, 0x06, 0x57, 0x68, - 0x6f, 0x41, 0x6d, 0x49, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x69, - 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x41, - 0x6d, 0x49, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, - 0x3b, 0x0a, 0x05, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, - 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x08, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x2a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, + 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x7b, 0x0a, 0x18, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x2d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, + 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, + 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x4a, 0x0a, 0x0a, 0x53, 0x65, 0x74, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x52, 0x0a, - 0x0e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, - 0x23, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x56, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, - 0x01, 0x12, 0x6f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, - 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, - 0x02, 0x01, 0x12, 0x5c, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x64, - 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, - 0x12, 0x4b, 0x0a, 0x07, 0x43, 0x61, 0x6e, 0x49, 0x55, 0x73, 0x65, 0x12, 0x1c, 0x2e, 0x69, 0x6f, - 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x49, 0x55, - 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, - 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x49, 0x55, 0x73, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x49, 0x0a, - 0x08, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, - 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, - 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x76, - 0x69, 0x65, 0x77, 0x12, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0xb0, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, - 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x44, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x64, 0x65, 0x66, 0x61, - 0x6e, 0x67, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x69, 0x6f, - 0x2f, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x65, 0x66, 0x61, 0x6e, - 0x67, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x49, 0x44, 0x58, 0xaa, 0x02, 0x0c, 0x49, 0x6f, 0x2e, 0x44, - 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x49, 0x6f, 0x5c, 0x44, 0x65, - 0x66, 0x61, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x49, 0x6f, 0x5c, 0x44, 0x65, 0x66, - 0x61, 0x6e, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x0e, 0x49, 0x6f, 0x3a, 0x3a, 0x44, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x43, 0x0a, 0x06, 0x57, 0x68, 0x6f, 0x41, 0x6d, 0x49, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, + 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x68, 0x6f, 0x41, 0x6d, 0x49, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x3b, 0x0a, 0x05, 0x54, 0x72, + 0x61, 0x63, 0x6b, 0x12, 0x1a, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x08, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x4d, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x52, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x23, 0x2e, 0x69, 0x6f, 0x2e, + 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x6f, 0x0a, 0x13, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, + 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5c, 0x0a, + 0x13, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x12, 0x28, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x03, 0x90, 0x02, 0x02, 0x12, 0x4b, 0x0a, 0x07, 0x43, + 0x61, 0x6e, 0x49, 0x55, 0x73, 0x65, 0x12, 0x1c, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, + 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x49, 0x55, 0x73, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x49, 0x55, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x49, 0x0a, 0x08, 0x45, 0x73, 0x74, 0x69, + 0x6d, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x1c, + 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x69, + 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xb0, 0x01, 0x0a, 0x10, + 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x6f, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2e, 0x76, 0x31, + 0x42, 0x0b, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x65, 0x66, 0x61, + 0x6e, 0x67, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x2f, 0x73, 0x72, + 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x69, 0x6f, 0x2f, 0x64, 0x65, 0x66, 0x61, + 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x76, 0x31, 0xa2, 0x02, + 0x03, 0x49, 0x44, 0x58, 0xaa, 0x02, 0x0c, 0x49, 0x6f, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x6e, 0x67, + 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x49, 0x6f, 0x5c, 0x44, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x5c, + 0x56, 0x31, 0xe2, 0x02, 0x18, 0x49, 0x6f, 0x5c, 0x44, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x5c, 0x56, + 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, + 0x49, 0x6f, 0x3a, 0x3a, 0x44, 0x65, 0x66, 0x61, 0x6e, 0x67, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/src/protos/io/defang/v1/fabric.proto b/src/protos/io/defang/v1/fabric.proto index 178750ebb..229e97b2a 100644 --- a/src/protos/io/defang/v1/fabric.proto +++ b/src/protos/io/defang/v1/fabric.proto @@ -15,7 +15,7 @@ service FabricController { }; // public rpc GetVersion(google.protobuf.Empty) returns (Version) { option idempotency_level = NO_SIDE_EFFECTS; - }; // public + }; // public rpc Token(TokenRequest) returns (TokenResponse); // public rpc RevokeToken(google.protobuf.Empty) returns (google.protobuf.Empty); @@ -145,6 +145,7 @@ enum Provider { AWS = 2; DIGITALOCEAN = 3; GCP = 4; + AZURE = 5; } message GetSelectedProviderRequest { string project = 1; } @@ -255,8 +256,7 @@ message GenerateFilesRequest { string language = 2; bool agree_tos = 3; bool training_opt_out = 4; // only valid for Pro users - string model_id = 5; // only valid for Pro users - + string model_id = 5; // only valid for Pro users } message File { @@ -399,7 +399,7 @@ message PutDeploymentRequest { Deployment deployment = 1; } message ListDeploymentsRequest { string project = 1; DeploymentType type = 2; // active or all - uint32 limit = 3; // number of deployments to return + uint32 limit = 3; // number of deployments to return } message ListDeploymentsResponse { repeated Deployment deployments = 1; } @@ -479,18 +479,21 @@ message ProjectUpdate { DeploymentMode mode = 6; Provider provider = 7; uint32 project_outputs_version = 8; - bytes project_outputs = 9; // JSON serialization of pulumi outputs. schema versioned using project_outputs_version + bytes project_outputs = 9; // JSON serialization of pulumi outputs. schema + // versioned using project_outputs_version } enum Platform { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files LINUX_AMD64 = 0; LINUX_ARM64 = 1; LINUX_ANY = 2; } message ServiceID { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files string name = 1; string project = 2; } @@ -501,30 +504,34 @@ message GetRequest { // was ServiceID } message Device { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files repeated string capabilities = 1; // "gpu", "tpu", etc. string driver = 2; // "nvidia", "amd", etc. uint32 count = 3; // number of devices to reserve } message Resource { - option deprecated = true; // still used by pulumi-defang provider in state files - float memory = 1; // in MiB - float cpus = 2; // fractional vCPUs + option deprecated = + true; // still used by pulumi-defang provider in state files + float memory = 1; // in MiB + float cpus = 2; // fractional vCPUs repeated Device devices = 3; // devices & capabilities } message Resources { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files Resource reservations = 1; // requested resources // Resource limits = 2; // hard limits } message Deploy { - option deprecated = true; // still used by pulumi-defang provider in state files - uint32 replicas = 1; // number of initial replicas - Resources resources = 2; // reservations and limits + option deprecated = + true; // still used by pulumi-defang provider in state files + uint32 replicas = 1; // number of initial replicas + Resources resources = 2; // reservations and limits // Placement placement = 3; // EndpointMode endpoint_mode @@ -532,8 +539,9 @@ message Deploy { } enum Protocol { - option deprecated = true; // still used by pulumi-defang provider in state files - ANY = 0; // unspecified means any protocol + option deprecated = + true; // still used by pulumi-defang provider in state files + ANY = 0; // unspecified means any protocol UDP = 1; TCP = 2; HTTP = 3; @@ -542,13 +550,15 @@ enum Protocol { } enum Mode { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files HOST = 0; // no load-balancer; suitable for internal services and functions INGRESS = 1; // with load-balancer; suitable for public services } message Port { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files uint32 target = 1; Protocol protocol = 2; Mode mode = 3; // load-balanced (ingress) or not (host) @@ -558,22 +568,25 @@ message Port { } message Secret { - option deprecated = true; // still used by pulumi-defang provider in state files - string source = 1; // name of the secret + option deprecated = + true; // still used by pulumi-defang provider in state files + string source = 1; // name of the secret // string target = 2; } message Build { - option deprecated = true; // still used by pulumi-defang provider in state files - string context = 1; // path or URL to the build context - string dockerfile = 2; // path to the Dockerfile + option deprecated = + true; // still used by pulumi-defang provider in state files + string context = 1; // path or URL to the build context + string dockerfile = 2; // path to the Dockerfile map args = 3; // build-time variables float shm_size = 4; // in MiB string target = 5; } message HealthCheck { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files repeated string test = 1; uint32 interval = 2; // in seconds uint32 timeout = 3; // in seconds; must be less than interval @@ -581,14 +594,16 @@ message HealthCheck { } enum Network { - option deprecated = true; // still used by pulumi-defang provider in state files - UNSPECIFIED = 0; // was: internal=false - PRIVATE = 1; // was: internal=true + option deprecated = + true; // still used by pulumi-defang provider in state files + UNSPECIFIED = 0; // was: internal=false + PRIVATE = 1; // was: internal=true PUBLIC = 2; } message Service { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files string name = 1; string image = 2; Platform platform = 3; @@ -612,7 +627,8 @@ message Service { } message StaticFiles { - option deprecated = true; // still used by pulumi-defang provider in state files + option deprecated = + true; // still used by pulumi-defang provider in state files string folder = 1; repeated string redirects = 2; } @@ -669,13 +685,9 @@ message DelegateSubdomainZoneRequest { message DelegateSubdomainZoneResponse { string zone = 1; } -message DeleteSubdomainZoneRequest { - string project = 1; -} +message DeleteSubdomainZoneRequest { string project = 1; } -message GetDelegateSubdomainZoneRequest { - string project = 1; -} +message GetDelegateSubdomainZoneRequest { string project = 1; } enum SubscriptionTier { SUBSCRIPTION_TIER_UNSPECIFIED = 0; @@ -729,6 +741,4 @@ message PreviewRequest { string project_name = 6; } -message PreviewResponse { - string etag = 1; -} +message PreviewResponse { string etag = 1; } From d5ba7c8efb6b3959d6efa3524ee64383910606fd Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Mon, 28 Jul 2025 15:50:40 -0700 Subject: [PATCH 2/7] Update cli.nix vendorHash --- .github/workflows/go.yml | 4 ++-- pkgs/defang/cli.nix | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index fb1f185dc..417d7da3b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -62,7 +62,7 @@ jobs: - name: Check nix-shell default.nix run: | set -o pipefail - nix-shell --pure -E 'with import {}; mkShell { buildInputs = [ (import ./default.nix {}) ]; }' --run defang 2>&1 | sed -u 's|\s\+got:|::error file=pkgs/defang/cli.nix,line=9::Replace the vendorHash in cli.nix with the correct value:|' + nix-shell --pure -E 'with import {}; mkShell { buildInputs = [ (import ./default.nix {}) ]; }' --run defang 2>&1 | sed -u 's|\s\+got:|::error file=pkgs/defang/cli.nix,line=10::Replace the vendorHash in cli.nix with the correct value:|' # go-byoc-test: # runs-on: ubuntu-latest @@ -321,7 +321,7 @@ jobs: push-docker: runs-on: ubuntu-latest needs: - - go-release + - go-release steps: - uses: actions/checkout@v4 diff --git a/pkgs/defang/cli.nix b/pkgs/defang/cli.nix index 70450635c..348434303 100644 --- a/pkgs/defang/cli.nix +++ b/pkgs/defang/cli.nix @@ -7,7 +7,7 @@ buildGoModule { pname = "defang-cli"; version = "git"; src = ../../src; - vendorHash = "sha256-4QMrneh4I2gTFf7erVnakqPDZakFzceGaN+ieAMNPX0="; # TODO: use fetchFromGitHub + vendorHash = "sha256-TiasfKgJyoxwl6B3uvTDFXI417WL8E1EYm/ljHLSFn4="; # TODO: use fetchFromGitHub subPackages = [ "cmd/cli" ]; From f0a97f4c4917cd87e6b44e07362ec824c3b28626 Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Mon, 28 Jul 2025 18:09:11 -0700 Subject: [PATCH 3/7] Add storage account creation --- src/go.mod | 19 ++-- src/go.sum | 38 ++++---- src/pkg/clouds/azure/aci/common.go | 24 ++--- src/pkg/clouds/azure/aci/run.go | 15 ++-- src/pkg/clouds/azure/aci/setup.go | 118 +++++++++++++++++++------ src/pkg/clouds/azure/aci/setup_test.go | 17 ++++ src/pkg/clouds/azure/common.go | 37 ++++++++ src/pkg/clouds/azure/job.go | 23 ++++- 8 files changed, 205 insertions(+), 86 deletions(-) diff --git a/src/go.mod b/src/go.mod index 0afcb5e98..93b10622d 100644 --- a/src/go.mod +++ b/src/go.mod @@ -20,6 +20,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 github.com/DefangLabs/secret-detector v0.0.0-20250108223530-c2b44d4c1f8f github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 github.com/aws/aws-sdk-go-v2 v1.32.6 @@ -39,7 +40,7 @@ require ( github.com/compose-spec/compose-go/v2 v2.7.2-0.20250715094302-8da9902241f9 github.com/digitalocean/godo v1.131.1 github.com/docker/docker v25.0.6+incompatible - github.com/golang-jwt/jwt/v5 v5.2.2 + github.com/golang-jwt/jwt/v5 v5.2.3 github.com/google/uuid v1.6.0 github.com/googleapis/gax-go/v2 v2.14.1 github.com/gorilla/websocket v1.5.0 @@ -55,10 +56,10 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.6 - golang.org/x/mod v0.18.0 + golang.org/x/mod v0.25.0 golang.org/x/oauth2 v0.29.0 - golang.org/x/sys v0.33.0 - golang.org/x/term v0.32.0 + golang.org/x/sys v0.34.0 + golang.org/x/term v0.33.0 google.golang.org/api v0.229.0 google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb google.golang.org/grpc v1.72.0 @@ -116,8 +117,8 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect gopkg.in/ini.v1 v1.66.2 // indirect @@ -162,8 +163,8 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/text v0.27.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.34.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index ddba8959a..734909cc3 100644 --- a/src/go.sum +++ b/src/go.sum @@ -50,6 +50,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1. github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0 h1:seyVIpxalxYmfjoo8MB4rRzWaobMG+KJ2+MAUrEvDGU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0/go.mod h1:M3QD7IyKZBaC4uAKjitTOSOXdcPC6JS1A9oOW3hYjbQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= @@ -190,8 +192,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= +github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -365,29 +367,29 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -400,18 +402,18 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -419,8 +421,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/pkg/clouds/azure/aci/common.go b/src/pkg/clouds/azure/aci/common.go index 642a5d2a9..1d7c19ca4 100644 --- a/src/pkg/clouds/azure/aci/common.go +++ b/src/pkg/clouds/azure/aci/common.go @@ -1,11 +1,9 @@ package aci import ( - "errors" "fmt" "os" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2" "github.com/DefangLabs/defang/src/pkg/clouds/azure" @@ -15,6 +13,7 @@ type ContainerInstance struct { azure.Azure containerGroupProps *armcontainerinstance.ContainerGroupPropertiesProperties resourceGroupName string + storageAccount string } func NewContainerInstance(resourceGroupName string, location azure.Location) *ContainerInstance { @@ -24,24 +23,11 @@ func NewContainerInstance(resourceGroupName string, location azure.Location) *Co return &ContainerInstance{ Azure: azure.Azure{Location: location}, resourceGroupName: resourceGroupName, // TODO: append location? + storageAccount: os.Getenv("DEFANG_CD_BUCKET"), } } -func newCreds() (string, *azidentity.DefaultAzureCredential, error) { - subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID") - if len(subscriptionID) == 0 { - return "", nil, errors.New("environment variable AZURE_SUBSCRIPTION_ID is not set") - } - - cred, err := azidentity.NewDefaultAzureCredential(nil) - if err != nil { - return "", nil, fmt.Errorf("failed to create default Azure credentials: %w", err) - } - - return subscriptionID, cred, nil -} - func newContainerGroupClient() (*armcontainerinstance.ContainerGroupsClient, error) { - subscriptionID, cred, err := newCreds() + subscriptionID, cred, err := azure.NewCreds() if err != nil { return nil, err } @@ -54,7 +40,7 @@ func newContainerGroupClient() (*armcontainerinstance.ContainerGroupsClient, err } func newContainerClient() (*armcontainerinstance.ContainersClient, error) { - subscriptionID, cred, err := newCreds() + subscriptionID, cred, err := azure.NewCreds() if err != nil { return nil, err } @@ -67,7 +53,7 @@ func newContainerClient() (*armcontainerinstance.ContainersClient, error) { } func newResourceGroupClient() (*armresources.ResourceGroupsClient, error) { - subscriptionID, cred, err := newCreds() + subscriptionID, cred, err := azure.NewCreds() if err != nil { return nil, err } diff --git a/src/pkg/clouds/azure/aci/run.go b/src/pkg/clouds/azure/aci/run.go index 998299477..1d2291b6a 100644 --- a/src/pkg/clouds/azure/aci/run.go +++ b/src/pkg/clouds/azure/aci/run.go @@ -22,29 +22,28 @@ func (c *ContainerInstance) Run(ctx context.Context, env map[string]string, args if err != nil { return nil, err } - var props armcontainerinstance.ContainerProperties + + commandArgs := to.SliceOfPtrs(args...) + var envVars []*armcontainerinstance.EnvironmentVariable for key, value := range env { - props.EnvironmentVariables = append(props.EnvironmentVariables, &armcontainerinstance.EnvironmentVariable{ + envVars = append(envVars, &armcontainerinstance.EnvironmentVariable{ Name: to.Ptr(key), Value: to.Ptr(value), }) } - for _, arg := range args { - props.Command = append(props.Command, to.Ptr(arg)) - } clone := *c.containerGroupProps for i, container := range clone.Containers { newProps := *container.Properties - newProps.Command = safeAppend(newProps.Command, props.Command...) - newProps.EnvironmentVariables = safeAppend(newProps.EnvironmentVariables, props.EnvironmentVariables...) + newProps.Command = safeAppend(newProps.Command, commandArgs...) // TODO: probably should only be done for the first container + newProps.EnvironmentVariables = safeAppend(newProps.EnvironmentVariables, envVars...) clone.Containers[i] = &armcontainerinstance.Container{ Name: container.Name, Properties: &newProps, } } - groupName := containerGroupName + "-" + pkg.RandomID() + groupName := containerGroupPrefix + pkg.RandomID() group := armcontainerinstance.ContainerGroup{ Name: to.Ptr(groupName), Location: c.Location.Ptr(), diff --git a/src/pkg/clouds/azure/aci/setup.go b/src/pkg/clouds/azure/aci/setup.go index 64bb2df0a..166ae0e3e 100644 --- a/src/pkg/clouds/azure/aci/setup.go +++ b/src/pkg/clouds/azure/aci/setup.go @@ -5,15 +5,19 @@ import ( "fmt" "math" "os" + "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" "github.com/DefangLabs/defang/src/pkg" + "github.com/DefangLabs/defang/src/pkg/clouds/azure" "github.com/DefangLabs/defang/src/pkg/types" ) -const containerGroupName = "defang-cd" +const containerGroupPrefix = "defang-cd-" +const storageAccountPrefix = "defangcd" func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Container) error { resourceGroupClient, err := newResourceGroupClient() @@ -41,38 +45,44 @@ func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Contai } for _, container := range containers { - cpus := math.Max(0.01, container.Cpus) // ensure minimum CPU is 0.01 - memoryInGB := math.Max(0.1, float64(container.Memory)/1024.0/1024.0/1024.0) // convert from B to GB - properties := &armcontainerinstance.ContainerProperties{ - Image: to.Ptr(container.Image), - Resources: &armcontainerinstance.ResourceRequirements{ - Requests: &armcontainerinstance.ResourceRequests{ - CPU: to.Ptr(math.Round(100*cpus) / 100), // round to 2 decimal places - MemoryInGB: to.Ptr(math.Round(10*memoryInGB) * 0.1), // Round to 1 decimal place + if container.IsInit { + properties := &armcontainerinstance.InitContainerPropertiesDefinition{ + Command: to.SliceOfPtrs(container.Command...), + Image: to.Ptr(container.Image), + } + c.containerGroupProps.InitContainers = append(c.containerGroupProps.InitContainers, &armcontainerinstance.InitContainerDefinition{ + Name: to.Ptr(container.Name), + Properties: properties, + }) + } else { + cpus := math.Max(0.01, container.Cpus) // ensure minimum CPU is 0.01 + memoryInGB := math.Max(0.1, float64(container.Memory)/1024.0/1024.0/1024.0) // convert from B to GB, minimum 0.1 + properties := &armcontainerinstance.ContainerProperties{ + Command: to.SliceOfPtrs(container.Command...), + Image: to.Ptr(container.Image), + Resources: &armcontainerinstance.ResourceRequirements{ + Requests: &armcontainerinstance.ResourceRequests{ + CPU: to.Ptr(math.Round(100*cpus) / 100), // round to 2 decimal places + MemoryInGB: to.Ptr(math.Round(10*memoryInGB) * 0.1), // Round to 1 decimal place + }, }, - }, + } + c.containerGroupProps.Containers = append(c.containerGroupProps.Containers, &armcontainerinstance.Container{ + Name: to.Ptr(container.Name), + Properties: properties, + }) } - for _, command := range container.Command { - properties.Command = append(properties.Command, to.Ptr(command)) - } - c.containerGroupProps.Containers = append(c.containerGroupProps.Containers, &armcontainerinstance.Container{ - Name: to.Ptr(container.Name), - Properties: properties, - }) } - // newContainerGroupClient, err := newContainerGroupClient() - // if err != nil { - // return err - // } - // deleteResponse, err := newContainerGroupClient.BeginDelete(ctx, c.resourceGroupName, containerGroupName, nil) - // if err != nil { - // return err - // } - // _, err = deleteResponse.PollUntilDone(ctx, nil) - // if err != nil { - // return err - // } + bucketName, err := c.getStorageAccount(ctx) + if err != nil { + return fmt.Errorf("failed to get bucket name: %w", err) + } + + if bucketName == "" { + + } + return nil } @@ -89,5 +99,55 @@ func (c *ContainerInstance) TearDown(ctx context.Context) error { if err != nil { return err } + + // TODO: delete storage account? return nil } + +func (c *ContainerInstance) getStorageAccount(ctx context.Context) (string, error) { + if c.storageAccount != "" { + return c.storageAccount, nil + } + + accountsClient, err := azure.NewStorageAccountsClient() + if err != nil { + return "", err + } + + for pager := accountsClient.NewListByResourceGroupPager(c.resourceGroupName, nil); pager.More(); { + page, err := pager.NextPage(ctx) + if err != nil { + return "", fmt.Errorf("failed to list storage accounts: %w", err) + } + for _, account := range page.Value { + if strings.HasPrefix(*account.Name, storageAccountPrefix) && *account.Location == c.Location.String() { + return *account.Name, nil + } + } + } + return "", nil +} + +func (c *ContainerInstance) setUpStorageAccount(ctx context.Context) (string, error) { + accountsClient, err := azure.NewStorageAccountsClient() + if err != nil { + return "", err + } + + storageAccount := storageAccountPrefix + pkg.RandomID() // unique storage account name + createResponse, err := accountsClient.BeginCreate(ctx, c.resourceGroupName, storageAccount, armstorage.AccountCreateParameters{ + Kind: to.Ptr(armstorage.KindStorageV2), + Location: c.Location.Ptr(), + SKU: &armstorage.SKU{Name: to.Ptr(armstorage.SKUNameStandardLRS)}, + }, nil) + if err != nil { + return "", fmt.Errorf("failed to create storage account: %w", err) + } + + _, err = createResponse.PollUntilDone(ctx, nil) + if err != nil { + return "", fmt.Errorf("failed to poll storage account creation: %w", err) + } + c.storageAccount = storageAccount + return storageAccount, nil +} diff --git a/src/pkg/clouds/azure/aci/setup_test.go b/src/pkg/clouds/azure/aci/setup_test.go index 7eef79fad..d87f7a9a3 100644 --- a/src/pkg/clouds/azure/aci/setup_test.go +++ b/src/pkg/clouds/azure/aci/setup_test.go @@ -40,3 +40,20 @@ func TestSetup(t *testing.T) { } }) } + +func TestStorage(t *testing.T) { + c := NewContainerInstance(testResourceGroupName, "westeurope") + + storageAccountName, err := c.setUpStorageAccount(context.Background()) + if err != nil { + t.Fatalf("Failed to set up storage account: %v", err) + } + + foundAccountName, err := c.getStorageAccount(context.Background()) + if err != nil { + t.Fatalf("Failed to get storage account name: %v", err) + } + if foundAccountName != storageAccountName { + t.Fatalf("Expected storage account name %s, got %s", storageAccountName, foundAccountName) + } +} diff --git a/src/pkg/clouds/azure/common.go b/src/pkg/clouds/azure/common.go index ca11cc393..0592e6792 100644 --- a/src/pkg/clouds/azure/common.go +++ b/src/pkg/clouds/azure/common.go @@ -1,5 +1,42 @@ package azure +import ( + "errors" + "fmt" + "os" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" +) + type Azure struct { Location Location } + +func NewCreds() (string, *azidentity.DefaultAzureCredential, error) { + subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID") + if len(subscriptionID) == 0 { + return "", nil, errors.New("environment variable AZURE_SUBSCRIPTION_ID is not set") + } + + cred, err := azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return "", nil, fmt.Errorf("failed to create default Azure credentials: %w", err) + } + + return subscriptionID, cred, nil +} + +func NewStorageAccountsClient() (*armstorage.AccountsClient, error) { + subscriptionID, cred, err := NewCreds() + if err != nil { + return nil, err + } + + storageClient, err := armstorage.NewClientFactory(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create storage client: %w", err) + } + + return storageClient.NewAccountsClient(), nil +} diff --git a/src/pkg/clouds/azure/job.go b/src/pkg/clouds/azure/job.go index 28ea4f9cc..1cbc782a6 100644 --- a/src/pkg/clouds/azure/job.go +++ b/src/pkg/clouds/azure/job.go @@ -12,9 +12,26 @@ type ContainerJob struct { // var _ types.Driver = (*ContainerJob)(nil) // CreateUploadURL implements types.Driver. -func (c *ContainerJob) CreateUploadURL(ctx context.Context, name string) (string, error) { - panic("unimplemented") -} +// func (c *ContainerJob) CreateUploadURL(ctx context.Context, name string) (string, error) { +// blobServiceClient, err := azblob.NewClient("", cred, nil) +// if err != nil { +// return "", err +// } + +// containerClient := blobServiceClient.NewContainerClient(c.Location) +// blobClient := containerClient.NewBlobClient(name) + +// url, err := blobClient.GetSASURL(ctx, azblob.BlobSASOptions{ +// Permissions: azblob.BlobSASPermissions{Read: true, Write: true}, +// Expiry: time.Now().Add(1 * time.Hour), +// }) +// if err != nil { +// return "", err +// } + +// return url, nil + +// } // GetInfo implements types.Driver. func (c *ContainerJob) GetInfo(ctx context.Context, taskID types.TaskID) (*types.TaskInfo, error) { From e3479c11209e2161a2e687f5b68f161f7ae20121 Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Tue, 29 Jul 2025 09:37:50 -0700 Subject: [PATCH 4/7] storage account --- src/go.mod | 1 + src/go.sum | 2 + src/pkg/cli/client/byoc/azure/byoc.go | 13 ++++++- src/pkg/clouds/azure/aci/common.go | 24 +++++++----- src/pkg/clouds/azure/aci/common_test.go | 4 +- src/pkg/clouds/azure/aci/run.go | 2 +- src/pkg/clouds/azure/aci/setup.go | 22 +++++------ src/pkg/clouds/azure/aci/setup_test.go | 17 --------- src/pkg/clouds/azure/aci/stop.go | 2 +- src/pkg/clouds/azure/aci/tail.go | 4 +- src/pkg/clouds/azure/common.go | 37 +++++++++++++------ src/pkg/clouds/azure/job.go | 49 ------------------------- 12 files changed, 70 insertions(+), 107 deletions(-) delete mode 100644 src/pkg/clouds/azure/job.go diff --git a/src/go.mod b/src/go.mod index 93b10622d..ee9cdb92f 100644 --- a/src/go.mod +++ b/src/go.mod @@ -21,6 +21,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 github.com/DefangLabs/secret-detector v0.0.0-20250108223530-c2b44d4c1f8f github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 github.com/aws/aws-sdk-go-v2 v1.32.6 diff --git a/src/go.sum b/src/go.sum index 734909cc3..71bd8a284 100644 --- a/src/go.sum +++ b/src/go.sum @@ -52,6 +52,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0/go.mod h1:M3QD7IyKZBaC4uAKjitTOSOXdcPC6JS1A9oOW3hYjbQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= diff --git a/src/pkg/cli/client/byoc/azure/byoc.go b/src/pkg/cli/client/byoc/azure/byoc.go index cf16f9c56..14186f433 100644 --- a/src/pkg/cli/client/byoc/azure/byoc.go +++ b/src/pkg/cli/client/byoc/azure/byoc.go @@ -2,7 +2,9 @@ package azure import ( "context" + "net/url" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" "github.com/DefangLabs/defang/src/pkg/cli/client" "github.com/DefangLabs/defang/src/pkg/cli/client/byoc" "github.com/DefangLabs/defang/src/pkg/clouds/azure/aci" @@ -28,7 +30,11 @@ func NewByocAzure(ctx context.Context, tenantName types.TenantName) *ByocAzure { // AccountInfo implements client.Provider. func (b *ByocAzure) AccountInfo(context.Context) (*client.AccountInfo, error) { - panic("unimplemented") + return &client.AccountInfo{ + AccountID: b.driver.SubscriptionID, + Provider: client.ProviderAzure, + Region: b.driver.Location.String(), + }, nil } // BootstrapCommand implements client.Provider. @@ -43,7 +49,10 @@ func (b *ByocAzure) BootstrapList(context.Context) ([]string, error) { // CreateUploadURL implements client.Provider. func (b *ByocAzure) CreateUploadURL(context.Context, *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) { - panic("unimplemented") + sasQueryParameters := sas.NewQueryParameters(url.Values{}, true) + return &defangv1.UploadURLResponse{ + Url: sasQueryParameters.Encode(), + }, nil } // Delete implements client.Provider. diff --git a/src/pkg/clouds/azure/aci/common.go b/src/pkg/clouds/azure/aci/common.go index 1d7c19ca4..cc0686c1b 100644 --- a/src/pkg/clouds/azure/aci/common.go +++ b/src/pkg/clouds/azure/aci/common.go @@ -21,44 +21,48 @@ func NewContainerInstance(resourceGroupName string, location azure.Location) *Co location = azure.Location(os.Getenv("AZURE_LOCATION")) } return &ContainerInstance{ - Azure: azure.Azure{Location: location}, + Azure: azure.Azure{ + Location: location, + SubscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"), + }, resourceGroupName: resourceGroupName, // TODO: append location? storageAccount: os.Getenv("DEFANG_CD_BUCKET"), } } -func newContainerGroupClient() (*armcontainerinstance.ContainerGroupsClient, error) { - subscriptionID, cred, err := azure.NewCreds() + +func (c ContainerInstance) newContainerGroupClient() (*armcontainerinstance.ContainerGroupsClient, error) { + cred, err := c.NewCreds() if err != nil { return nil, err } - clientFactory, err := armcontainerinstance.NewClientFactory(subscriptionID, cred, nil) + clientFactory, err := armcontainerinstance.NewClientFactory(c.SubscriptionID, cred, nil) if err != nil { return nil, fmt.Errorf("failed to create container group client: %w", err) } return clientFactory.NewContainerGroupsClient(), nil } -func newContainerClient() (*armcontainerinstance.ContainersClient, error) { - subscriptionID, cred, err := azure.NewCreds() +func (c ContainerInstance) newContainerClient() (*armcontainerinstance.ContainersClient, error) { + cred, err := c.NewCreds() if err != nil { return nil, err } - clientFactory, err := armcontainerinstance.NewClientFactory(subscriptionID, cred, nil) + clientFactory, err := armcontainerinstance.NewClientFactory(c.SubscriptionID, cred, nil) if err != nil { return nil, fmt.Errorf("failed to create container client: %w", err) } return clientFactory.NewContainersClient(), nil } -func newResourceGroupClient() (*armresources.ResourceGroupsClient, error) { - subscriptionID, cred, err := azure.NewCreds() +func (c ContainerInstance) newResourceGroupClient() (*armresources.ResourceGroupsClient, error) { + cred, err := c.NewCreds() if err != nil { return nil, err } - resourcesClientFactory, err := armresources.NewClientFactory(subscriptionID, cred, nil) + resourcesClientFactory, err := armresources.NewClientFactory(c.SubscriptionID, cred, nil) if err != nil { return nil, fmt.Errorf("failed to create resource group client: %w", err) } diff --git a/src/pkg/clouds/azure/aci/common_test.go b/src/pkg/clouds/azure/aci/common_test.go index 670d0bb9e..e0935f021 100644 --- a/src/pkg/clouds/azure/aci/common_test.go +++ b/src/pkg/clouds/azure/aci/common_test.go @@ -9,7 +9,9 @@ import ( func TestNewClient(t *testing.T) { t.Setenv("AZURE_SUBSCRIPTION_ID", uuid.NewString()) - client, err := newContainerGroupClient() + c := NewContainerInstance(testResourceGroupName, "") + + client, err := c.newContainerGroupClient() if err != nil { t.Fatalf("Failed to create client: %v", err) } diff --git a/src/pkg/clouds/azure/aci/run.go b/src/pkg/clouds/azure/aci/run.go index 1d2291b6a..c38664c05 100644 --- a/src/pkg/clouds/azure/aci/run.go +++ b/src/pkg/clouds/azure/aci/run.go @@ -18,7 +18,7 @@ func safeAppend[T any](slice []T, elems ...T) []T { } func (c *ContainerInstance) Run(ctx context.Context, env map[string]string, args ...string) (ContainerGroupName, error) { - containerGroupClient, err := newContainerGroupClient() + containerGroupClient, err := c.newContainerGroupClient() if err != nil { return nil, err } diff --git a/src/pkg/clouds/azure/aci/setup.go b/src/pkg/clouds/azure/aci/setup.go index 166ae0e3e..a3d217a3a 100644 --- a/src/pkg/clouds/azure/aci/setup.go +++ b/src/pkg/clouds/azure/aci/setup.go @@ -12,7 +12,6 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" "github.com/DefangLabs/defang/src/pkg" - "github.com/DefangLabs/defang/src/pkg/clouds/azure" "github.com/DefangLabs/defang/src/pkg/types" ) @@ -20,7 +19,7 @@ const containerGroupPrefix = "defang-cd-" const storageAccountPrefix = "defangcd" func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Container) error { - resourceGroupClient, err := newResourceGroupClient() + resourceGroupClient, err := c.newResourceGroupClient() if err != nil { return err } @@ -74,12 +73,12 @@ func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Contai } } - bucketName, err := c.getStorageAccount(ctx) + storageAccount, err := c.setUpStorageAccount(ctx) if err != nil { return fmt.Errorf("failed to get bucket name: %w", err) } - if bucketName == "" { + if storageAccount == "" { } @@ -87,7 +86,7 @@ func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Contai } func (c *ContainerInstance) TearDown(ctx context.Context) error { - resourceGroupClient, err := newResourceGroupClient() + resourceGroupClient, err := c.newResourceGroupClient() if err != nil { return err } @@ -104,16 +103,11 @@ func (c *ContainerInstance) TearDown(ctx context.Context) error { return nil } -func (c *ContainerInstance) getStorageAccount(ctx context.Context) (string, error) { +func (c *ContainerInstance) getStorageAccount(ctx context.Context, accountsClient *armstorage.AccountsClient) (string, error) { if c.storageAccount != "" { return c.storageAccount, nil } - accountsClient, err := azure.NewStorageAccountsClient() - if err != nil { - return "", err - } - for pager := accountsClient.NewListByResourceGroupPager(c.resourceGroupName, nil); pager.More(); { page, err := pager.NextPage(ctx) if err != nil { @@ -129,11 +123,15 @@ func (c *ContainerInstance) getStorageAccount(ctx context.Context) (string, erro } func (c *ContainerInstance) setUpStorageAccount(ctx context.Context) (string, error) { - accountsClient, err := azure.NewStorageAccountsClient() + accountsClient, err := c.NewStorageAccountsClient() if err != nil { return "", err } + if x, err := c.getStorageAccount(ctx, accountsClient); err == nil { + return x, nil + } + storageAccount := storageAccountPrefix + pkg.RandomID() // unique storage account name createResponse, err := accountsClient.BeginCreate(ctx, c.resourceGroupName, storageAccount, armstorage.AccountCreateParameters{ Kind: to.Ptr(armstorage.KindStorageV2), diff --git a/src/pkg/clouds/azure/aci/setup_test.go b/src/pkg/clouds/azure/aci/setup_test.go index d87f7a9a3..7eef79fad 100644 --- a/src/pkg/clouds/azure/aci/setup_test.go +++ b/src/pkg/clouds/azure/aci/setup_test.go @@ -40,20 +40,3 @@ func TestSetup(t *testing.T) { } }) } - -func TestStorage(t *testing.T) { - c := NewContainerInstance(testResourceGroupName, "westeurope") - - storageAccountName, err := c.setUpStorageAccount(context.Background()) - if err != nil { - t.Fatalf("Failed to set up storage account: %v", err) - } - - foundAccountName, err := c.getStorageAccount(context.Background()) - if err != nil { - t.Fatalf("Failed to get storage account name: %v", err) - } - if foundAccountName != storageAccountName { - t.Fatalf("Expected storage account name %s, got %s", storageAccountName, foundAccountName) - } -} diff --git a/src/pkg/clouds/azure/aci/stop.go b/src/pkg/clouds/azure/aci/stop.go index 3c82929a8..e7c168269 100644 --- a/src/pkg/clouds/azure/aci/stop.go +++ b/src/pkg/clouds/azure/aci/stop.go @@ -5,7 +5,7 @@ import ( ) func (c *ContainerInstance) Stop(ctx context.Context, groupName ContainerGroupName) error { - containerGroupClient, err := newContainerGroupClient() + containerGroupClient, err := c.newContainerGroupClient() if err != nil { return err } diff --git a/src/pkg/clouds/azure/aci/tail.go b/src/pkg/clouds/azure/aci/tail.go index 26816fbdb..998087504 100644 --- a/src/pkg/clouds/azure/aci/tail.go +++ b/src/pkg/clouds/azure/aci/tail.go @@ -42,7 +42,7 @@ func (c *ContainerInstance) Tail(ctx context.Context, groupName ContainerGroupNa } func (c *ContainerInstance) QueryLogs(ctx context.Context, groupName ContainerGroupName, containerName string) (string, error) { - client, err := newContainerClient() + client, err := c.newContainerClient() if err != nil { return "", err } @@ -65,7 +65,7 @@ func (c *ContainerInstance) QueryLogs(ctx context.Context, groupName ContainerGr } func (c *ContainerInstance) StreamLogs(ctx context.Context, groupName ContainerGroupName, containerName string) (<-chan logEntry, error) { - client, err := newContainerClient() + client, err := c.newContainerClient() if err != nil { return nil, err } diff --git a/src/pkg/clouds/azure/common.go b/src/pkg/clouds/azure/common.go index 0592e6792..b13b9158f 100644 --- a/src/pkg/clouds/azure/common.go +++ b/src/pkg/clouds/azure/common.go @@ -3,40 +3,53 @@ package azure import ( "errors" "fmt" - "os" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" ) type Azure struct { - Location Location + Location Location + SubscriptionID string } -func NewCreds() (string, *azidentity.DefaultAzureCredential, error) { - subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID") - if len(subscriptionID) == 0 { - return "", nil, errors.New("environment variable AZURE_SUBSCRIPTION_ID is not set") +func (a Azure) NewCreds() (*azidentity.DefaultAzureCredential, error) { + if len(a.SubscriptionID) == 0 { + return nil, errors.New("environment variable AZURE_SUBSCRIPTION_ID is not set") } cred, err := azidentity.NewDefaultAzureCredential(nil) if err != nil { - return "", nil, fmt.Errorf("failed to create default Azure credentials: %w", err) + return nil, fmt.Errorf("failed to create default Azure credentials: %w", err) } - return subscriptionID, cred, nil + return cred, nil } -func NewStorageAccountsClient() (*armstorage.AccountsClient, error) { - subscriptionID, cred, err := NewCreds() +func (a Azure) NewStorageAccountsClient() (*armstorage.AccountsClient, error) { + cred, err := a.NewCreds() if err != nil { return nil, err } - storageClient, err := armstorage.NewClientFactory(subscriptionID, cred, nil) + clientFactory, err := armstorage.NewClientFactory(a.SubscriptionID, cred, nil) if err != nil { return nil, fmt.Errorf("failed to create storage client: %w", err) } - return storageClient.NewAccountsClient(), nil + return clientFactory.NewAccountsClient(), nil +} + +func (a Azure) NewStorageClient() (*armstorage.BlobContainersClient, error) { + cred, err := a.NewCreds() + if err != nil { + return nil, err + } + + clientFactory, err := armstorage.NewClientFactory(a.SubscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("failed to create storage client: %w", err) + } + + return clientFactory.NewBlobContainersClient(), nil } diff --git a/src/pkg/clouds/azure/job.go b/src/pkg/clouds/azure/job.go deleted file mode 100644 index 1cbc782a6..000000000 --- a/src/pkg/clouds/azure/job.go +++ /dev/null @@ -1,49 +0,0 @@ -package azure - -import ( - "context" - - "github.com/DefangLabs/defang/src/pkg/types" -) - -type ContainerJob struct { -} - -// var _ types.Driver = (*ContainerJob)(nil) - -// CreateUploadURL implements types.Driver. -// func (c *ContainerJob) CreateUploadURL(ctx context.Context, name string) (string, error) { -// blobServiceClient, err := azblob.NewClient("", cred, nil) -// if err != nil { -// return "", err -// } - -// containerClient := blobServiceClient.NewContainerClient(c.Location) -// blobClient := containerClient.NewBlobClient(name) - -// url, err := blobClient.GetSASURL(ctx, azblob.BlobSASOptions{ -// Permissions: azblob.BlobSASPermissions{Read: true, Write: true}, -// Expiry: time.Now().Add(1 * time.Hour), -// }) -// if err != nil { -// return "", err -// } - -// return url, nil - -// } - -// GetInfo implements types.Driver. -func (c *ContainerJob) GetInfo(ctx context.Context, taskID types.TaskID) (*types.TaskInfo, error) { - panic("unimplemented") -} - -// ListSecrets implements types.Driver. -func (c *ContainerJob) ListSecrets(ctx context.Context) ([]string, error) { - panic("unimplemented") -} - -// PutSecret implements types.Driver. -func (c *ContainerJob) PutSecret(ctx context.Context, name string, value string) error { - panic("unimplemented") -} From 86ade1ab2047b8d286598163801414a3faabb57c Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Wed, 6 Aug 2025 15:07:34 -0700 Subject: [PATCH 5/7] fix(azure): context upload --- src/go.mod | 13 ++-- src/go.sum | 28 ++++----- src/pkg/cli/client/byoc/azure/byoc.go | 61 +++++++++--------- src/pkg/cli/client/client.go | 1 - src/pkg/cli/client/playground.go | 1 + src/pkg/cli/client/provider.go | 1 + src/pkg/cli/compose/context.go | 9 ++- src/pkg/cli/connect.go | 3 + src/pkg/clouds/azure/aci/common.go | 5 +- src/pkg/clouds/azure/aci/setup.go | 82 ++++++++++++++++++------- src/pkg/clouds/azure/aci/upload.go | 75 ++++++++++++++++++++++ src/pkg/clouds/azure/aci/upload_test.go | 43 +++++++++++++ src/pkg/clouds/azure/common.go | 16 ++++- src/pkg/http/post.go | 8 ++- src/pkg/http/put.go | 6 +- 15 files changed, 272 insertions(+), 80 deletions(-) create mode 100644 src/pkg/clouds/azure/aci/upload.go create mode 100644 src/pkg/clouds/azure/aci/upload_test.go diff --git a/src/go.mod b/src/go.mod index ee9cdb92f..999acfe65 100644 --- a/src/go.mod +++ b/src/go.mod @@ -16,7 +16,7 @@ require ( cloud.google.com/go/secretmanager v1.14.5 cloud.google.com/go/storage v1.50.0 github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources/v2 v2.1.0 @@ -76,7 +76,7 @@ require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/longrunning v0.6.6 // indirect cloud.google.com/go/monitoring v1.24.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect @@ -106,6 +106,7 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect @@ -145,7 +146,7 @@ require ( github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -159,11 +160,11 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect golang.org/x/sync v0.16.0 // indirect golang.org/x/text v0.27.0 // indirect golang.org/x/time v0.11.0 // indirect diff --git a/src/go.sum b/src/go.sum index 71bd8a284..39b3f1b1c 100644 --- a/src/go.sum +++ b/src/go.sum @@ -32,14 +32,14 @@ cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2 h1:Hr5FTipp7SL07o2FvoVOX9HRiRH3CR3Mj8pxqCcdD5A= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0 h1:+dIXMjlifRbG3d01DF8dwckUSXADuW5dgBNt1fbkpv0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance/v2 v2.4.0/go.mod h1:FN0UJ15tJ7kV7JYrYAleEq44Ew1cUiyLcJrfrTxHGd0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= @@ -182,8 +182,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -306,8 +306,8 @@ github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/ross96D/cancelreader v0.2.6 h1:XLPWassoMWRTlHvEoVKS3z0N0a7jHcIupGU0U1gNArw= github.com/ross96D/cancelreader v0.2.6/go.mod h1:sSs12d88ds2FFb9aOsKYyhTJCJDQDOll6gpyILZwIzc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -345,22 +345,22 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/src/pkg/cli/client/byoc/azure/byoc.go b/src/pkg/cli/client/byoc/azure/byoc.go index 14186f433..ea9f7b9bc 100644 --- a/src/pkg/cli/client/byoc/azure/byoc.go +++ b/src/pkg/cli/client/byoc/azure/byoc.go @@ -2,9 +2,8 @@ package azure import ( "context" - "net/url" + "errors" - "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" "github.com/DefangLabs/defang/src/pkg/cli/client" "github.com/DefangLabs/defang/src/pkg/cli/client/byoc" "github.com/DefangLabs/defang/src/pkg/clouds/azure/aci" @@ -20,7 +19,7 @@ type ByocAzure struct { var _ client.Provider = (*ByocAzure)(nil) -func NewByocAzure(ctx context.Context, tenantName types.TenantName) *ByocAzure { +func NewByocProvider(ctx context.Context, tenantName types.TenantName) *ByocAzure { b := &ByocAzure{ driver: aci.NewContainerInstance("defang-cd", ""), // default location => from AZURE_LOCATION env var } @@ -39,113 +38,119 @@ func (b *ByocAzure) AccountInfo(context.Context) (*client.AccountInfo, error) { // BootstrapCommand implements client.Provider. func (b *ByocAzure) BootstrapCommand(context.Context, client.BootstrapCommandRequest) (types.ETag, error) { - panic("unimplemented") + return "", errors.ErrUnsupported } // BootstrapList implements client.Provider. func (b *ByocAzure) BootstrapList(context.Context) ([]string, error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // CreateUploadURL implements client.Provider. -func (b *ByocAzure) CreateUploadURL(context.Context, *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) { - sasQueryParameters := sas.NewQueryParameters(url.Values{}, true) +func (b *ByocAzure) CreateUploadURL(ctx context.Context, req *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) { + url, err := b.driver.CreateUploadURL(ctx, req.Digest) + if err != nil { + return nil, err + } + return &defangv1.UploadURLResponse{ - Url: sasQueryParameters.Encode(), + Url: url, }, nil } // Delete implements client.Provider. func (b *ByocAzure) Delete(context.Context, *defangv1.DeleteRequest) (*defangv1.DeleteResponse, error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // DeleteConfig implements client.Provider. func (b *ByocAzure) DeleteConfig(context.Context, *defangv1.Secrets) error { - panic("unimplemented") + return errors.ErrUnsupported } // Deploy implements client.Provider. -func (b *ByocAzure) Deploy(context.Context, *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { - panic("unimplemented") +func (b *ByocAzure) Deploy(ctx context.Context, req *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { + // return nil, errors.ErrUnsupported + return &defangv1.DeployResponse{}, byoc.DebugPulumi(ctx, nil, "up", "payload") } // Destroy implements client.Provider. func (b *ByocAzure) Destroy(context.Context, *defangv1.DestroyRequest) (types.ETag, error) { - panic("unimplemented") + return "", errors.ErrUnsupported } // GetDeploymentStatus implements client.Provider. func (b *ByocAzure) GetDeploymentStatus(context.Context) error { - panic("unimplemented") + return errors.ErrUnsupported } // GetProjectUpdate implements client.Provider. func (b *ByocAzure) GetProjectUpdate(context.Context, string) (*defangv1.ProjectUpdate, error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // GetService implements client.Provider. func (b *ByocAzure) GetService(context.Context, *defangv1.GetRequest) (*defangv1.ServiceInfo, error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // GetServices implements client.Provider. func (b *ByocAzure) GetServices(context.Context, *defangv1.GetServicesRequest) (*defangv1.GetServicesResponse, error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // ListConfig implements client.Provider. func (b *ByocAzure) ListConfig(context.Context, *defangv1.ListConfigsRequest) (*defangv1.Secrets, error) { - panic("unimplemented") + // return nil, errors.ErrUnsupported + return &defangv1.Secrets{}, nil } // PrepareDomainDelegation implements client.Provider. func (b *ByocAzure) PrepareDomainDelegation(context.Context, client.PrepareDomainDelegationRequest) (*client.PrepareDomainDelegationResponse, error) { - panic("unimplemented") + return nil, nil // TODO: implement domain delegation for Azure } // Preview implements client.Provider. func (b *ByocAzure) Preview(context.Context, *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // PutConfig implements client.Provider. func (b *ByocAzure) PutConfig(context.Context, *defangv1.PutConfigRequest) error { - panic("unimplemented") + return errors.ErrUnsupported } // QueryForDebug implements client.Provider. func (b *ByocAzure) QueryForDebug(context.Context, *defangv1.DebugRequest) error { - panic("unimplemented") + return errors.ErrUnsupported } // QueryLogs implements client.Provider. func (b *ByocAzure) QueryLogs(context.Context, *defangv1.TailRequest) (client.ServerStream[defangv1.TailResponse], error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // RemoteProjectName implements client.Provider. // Subtle: this method shadows the method (*ByocBaseClient).RemoteProjectName of ByocAzure.ByocBaseClient. func (b *ByocAzure) RemoteProjectName(context.Context) (string, error) { - panic("unimplemented") + return "", errors.ErrUnsupported } // ServiceDNS implements client.Provider. // Subtle: this method shadows the method (*ByocBaseClient).ServiceDNS of ByocAzure.ByocBaseClient. -func (b *ByocAzure) ServiceDNS(string) string { - panic("unimplemented") +func (b *ByocAzure) ServiceDNS(host string) string { + return host } // SetCanIUseConfig implements client.Provider. // Subtle: this method shadows the method (*ByocBaseClient).SetCanIUseConfig of ByocAzure.ByocBaseClient. func (b *ByocAzure) SetCanIUseConfig(*defangv1.CanIUseResponse) { - panic("unimplemented") + // return nil, errors.ErrUnsupported } // Subscribe implements client.Provider. func (b *ByocAzure) Subscribe(context.Context, *defangv1.SubscribeRequest) (client.ServerStream[defangv1.SubscribeResponse], error) { - panic("unimplemented") + return nil, errors.ErrUnsupported } // TearDown implements client.Provider. diff --git a/src/pkg/cli/client/client.go b/src/pkg/cli/client/client.go index 4f8397d9e..cebc6a5b3 100644 --- a/src/pkg/cli/client/client.go +++ b/src/pkg/cli/client/client.go @@ -34,7 +34,6 @@ type FabricClient interface { PutDeployment(context.Context, *defangv1.PutDeploymentRequest) error RevokeToken(context.Context) error SetSelectedProvider(context.Context, *defangv1.SetSelectedProviderRequest) error - // Subscribe(context.Context, *v1.SubscribeRequest) (*v1.SubscribeResponse, error) Token(context.Context, *defangv1.TokenRequest) (*defangv1.TokenResponse, error) Track(string, ...Property) error VerifyDNSSetup(context.Context, *defangv1.VerifyDNSSetupRequest) error diff --git a/src/pkg/cli/client/playground.go b/src/pkg/cli/client/playground.go index 9dd02d1b8..c932705ee 100644 --- a/src/pkg/cli/client/playground.go +++ b/src/pkg/cli/client/playground.go @@ -128,4 +128,5 @@ func (g *PlaygroundProvider) QueryForDebug(ctx context.Context, req *defangv1.De func (g *PlaygroundProvider) PrepareDomainDelegation(ctx context.Context, req PrepareDomainDelegationRequest) (*PrepareDomainDelegationResponse, error) { return nil, nil // Playground does not support delegate domains } + func (g *PlaygroundProvider) SetCanIUseConfig(*defangv1.CanIUseResponse) {} diff --git a/src/pkg/cli/client/provider.go b/src/pkg/cli/client/provider.go index 93146cf10..192df7dc2 100644 --- a/src/pkg/cli/client/provider.go +++ b/src/pkg/cli/client/provider.go @@ -58,6 +58,7 @@ type Provider interface { SetCanIUseConfig(*defangv1.CanIUseResponse) Subscribe(context.Context, *defangv1.SubscribeRequest) (ServerStream[defangv1.SubscribeResponse], error) TearDown(context.Context) error + // Upload(context.Context, *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) } type Loader interface { diff --git a/src/pkg/cli/compose/context.go b/src/pkg/cli/compose/context.go index 18fb6bb42..b07acf9be 100644 --- a/src/pkg/cli/compose/context.go +++ b/src/pkg/cli/compose/context.go @@ -232,16 +232,19 @@ func uploadArchive(ctx context.Context, provider client.Provider, project string } // Do an HTTP PUT to the generated URL - resp, err := http.Put(ctx, res.Url, string(contentType), body) + header := http.Header{"Content-Type": []string{string(contentType)}} + header.Set("X-Ms-Blob-Type", "BlockBlob") // HACK: move to provider + resp, err := http.PutWithHeader(ctx, res.Url, header, body) if err != nil { return "", err } defer resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != 200 && resp.StatusCode != 201 { return "", fmt.Errorf("HTTP PUT failed with status code %v", resp.Status) } - url := http.RemoveQueryParam(res.Url) + url := http.RemoveQueryParam(res.Url) // remove any access signature + const gcpPrefix = "https://storage.googleapis.com/" if strings.HasPrefix(url, gcpPrefix) { url = "gs://" + url[len(gcpPrefix):] diff --git a/src/pkg/cli/connect.go b/src/pkg/cli/connect.go index 0d80f9424..fb8d19921 100644 --- a/src/pkg/cli/connect.go +++ b/src/pkg/cli/connect.go @@ -8,6 +8,7 @@ import ( "github.com/DefangLabs/defang/src/pkg" "github.com/DefangLabs/defang/src/pkg/cli/client" "github.com/DefangLabs/defang/src/pkg/cli/client/byoc/aws" + "github.com/DefangLabs/defang/src/pkg/cli/client/byoc/azure" "github.com/DefangLabs/defang/src/pkg/cli/client/byoc/do" "github.com/DefangLabs/defang/src/pkg/cli/client/byoc/gcp" "github.com/DefangLabs/defang/src/pkg/term" @@ -81,6 +82,8 @@ func NewProvider(ctx context.Context, providerID client.ProviderID, fabricClient provider = do.NewByocProvider(ctx, fabricClient.GetTenantName()) case client.ProviderGCP: provider = gcp.NewByocProvider(ctx, fabricClient.GetTenantName()) + case client.ProviderAzure: + provider = azure.NewByocProvider(ctx, fabricClient.GetTenantName()) default: provider = &client.PlaygroundProvider{FabricClient: fabricClient} } diff --git a/src/pkg/clouds/azure/aci/common.go b/src/pkg/clouds/azure/aci/common.go index cc0686c1b..c7239dd83 100644 --- a/src/pkg/clouds/azure/aci/common.go +++ b/src/pkg/clouds/azure/aci/common.go @@ -13,7 +13,8 @@ type ContainerInstance struct { azure.Azure containerGroupProps *armcontainerinstance.ContainerGroupPropertiesProperties resourceGroupName string - storageAccount string + StorageAccount string + BlobContainerName string } func NewContainerInstance(resourceGroupName string, location azure.Location) *ContainerInstance { @@ -26,7 +27,7 @@ func NewContainerInstance(resourceGroupName string, location azure.Location) *Co SubscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"), }, resourceGroupName: resourceGroupName, // TODO: append location? - storageAccount: os.Getenv("DEFANG_CD_BUCKET"), + StorageAccount: os.Getenv("DEFANG_CD_BUCKET"), } } diff --git a/src/pkg/clouds/azure/aci/setup.go b/src/pkg/clouds/azure/aci/setup.go index a3d217a3a..a1a4d99b4 100644 --- a/src/pkg/clouds/azure/aci/setup.go +++ b/src/pkg/clouds/azure/aci/setup.go @@ -17,6 +17,7 @@ import ( const containerGroupPrefix = "defang-cd-" const storageAccountPrefix = "defangcd" +const blobContainerName = "uploads" func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Container) error { resourceGroupClient, err := c.newResourceGroupClient() @@ -73,13 +74,9 @@ func (c *ContainerInstance) SetUp(ctx context.Context, containers []types.Contai } } - storageAccount, err := c.setUpStorageAccount(ctx) + _, err = c.SetUpStorageAccount(ctx) if err != nil { - return fmt.Errorf("failed to get bucket name: %w", err) - } - - if storageAccount == "" { - + return fmt.Errorf("failed to get storage account name: %w", err) } return nil @@ -104,8 +101,8 @@ func (c *ContainerInstance) TearDown(ctx context.Context) error { } func (c *ContainerInstance) getStorageAccount(ctx context.Context, accountsClient *armstorage.AccountsClient) (string, error) { - if c.storageAccount != "" { - return c.storageAccount, nil + if c.StorageAccount != "" { + return c.StorageAccount, nil } for pager := accountsClient.NewListByResourceGroupPager(c.resourceGroupName, nil); pager.More(); { @@ -122,30 +119,71 @@ func (c *ContainerInstance) getStorageAccount(ctx context.Context, accountsClien return "", nil } -func (c *ContainerInstance) setUpStorageAccount(ctx context.Context) (string, error) { +func (c *ContainerInstance) SetUpStorageAccount(ctx context.Context) (string, error) { accountsClient, err := c.NewStorageAccountsClient() if err != nil { return "", err } - if x, err := c.getStorageAccount(ctx, accountsClient); err == nil { - return x, nil + storageAccount, err := c.getStorageAccount(ctx, accountsClient) + if err != nil { + return "", err } - storageAccount := storageAccountPrefix + pkg.RandomID() // unique storage account name - createResponse, err := accountsClient.BeginCreate(ctx, c.resourceGroupName, storageAccount, armstorage.AccountCreateParameters{ - Kind: to.Ptr(armstorage.KindStorageV2), - Location: c.Location.Ptr(), - SKU: &armstorage.SKU{Name: to.Ptr(armstorage.SKUNameStandardLRS)}, - }, nil) + if storageAccount == "" { + storageAccount = storageAccountPrefix + pkg.RandomID() // unique storage account name + createResponse, err := accountsClient.BeginCreate(ctx, c.resourceGroupName, storageAccount, armstorage.AccountCreateParameters{ + Kind: to.Ptr(armstorage.KindStorageV2), + Location: c.Location.Ptr(), + SKU: &armstorage.SKU{Name: to.Ptr(armstorage.SKUNameStandardLRS)}, + }, nil) + if err != nil { + return "", fmt.Errorf("failed to create storage account: %w", err) + } + + _, err = createResponse.PollUntilDone(ctx, nil) + if err != nil { + return "", fmt.Errorf("failed to poll storage account creation: %w", err) + } + } + c.StorageAccount = storageAccount + + // Assign permissions + // objectId, err := getCurrentUserObjectID(ctx) + // if err != nil { + // return "", fmt.Errorf("failed to get current user object ID: %w", err) + // } + // println("Current user object ID:", objectId) + + // if err := c.assignBlobDataContributor(ctx, objectId); err != nil { + // return "", fmt.Errorf("failed to assign blob data contributor role: %w", err) + // } + + // client, err := c.NewStorageAccountsClient() + // if err != nil { + // return "", fmt.Errorf("failed to create storage accounts client: %w", err) + // } + // lk, err := client.ListKeys(ctx, c.resourceGroupName, storageAccount, nil) + // if err != nil { + // return "", fmt.Errorf("failed to list storage account keys: %w", err) + // } + + // ss, err := client.RegenerateKey(ctx, c.resourceGroupName, storageAccount, armstorage.RegenerateKeyParameters{ + // KeyName: to.Ptr("key1"), + // }, nil) + // if err != nil { + // return "", fmt.Errorf("failed to regenerate storage account key: %w", err) + // } + + containerClient, err := c.NewBlobContainersClient() if err != nil { - return "", fmt.Errorf("failed to create storage account: %w", err) + return "", fmt.Errorf("failed to create storage client: %w", err) } - - _, err = createResponse.PollUntilDone(ctx, nil) + container, err := containerClient.Create(ctx, c.resourceGroupName, storageAccount, blobContainerName, armstorage.BlobContainer{}, nil) if err != nil { - return "", fmt.Errorf("failed to poll storage account creation: %w", err) + return "", fmt.Errorf("failed to create blob container: %w", err) } - c.storageAccount = storageAccount + c.BlobContainerName = *container.Name + return storageAccount, nil } diff --git a/src/pkg/clouds/azure/aci/upload.go b/src/pkg/clouds/azure/aci/upload.go new file mode 100644 index 000000000..576df0927 --- /dev/null +++ b/src/pkg/clouds/azure/aci/upload.go @@ -0,0 +1,75 @@ +package aci + +import ( + "context" + "errors" + "fmt" + "net/url" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas" + "github.com/google/uuid" +) + +func (c *ContainerInstance) CreateUploadURL(ctx context.Context, blobName string) (string, error) { + if blobName == "" { + blobName = uuid.NewString() + } else { + if len(blobName) > 64 { + return "", errors.New("name must be less than 64 characters") + } + // Sanitize the digest so it's safe to use as a file name + blobName = strings.ReplaceAll(blobName, "/", "_") + // name = path.Join(buildsPath, tenantName.String(), digest); TODO: avoid collisions between tenants + } + if _, err := c.SetUpStorageAccount(ctx); err != nil { + return "", err + } + + now := time.Now().UTC() + expiry := now.Add(1 * time.Hour) + + // TODO: using user delegation is more secure than shared key, but requires the user to reauth to a OAuth2 client with the appropriate permissions to discover the user's ObjectID + // userCred, err := client.GetUserDelegationCredential(ctx, service.KeyInfo{ + // Start: to.Ptr(now.Format(time.RFC3339)), + // Expiry: to.Ptr(expiry.Format(time.RFC3339)), + // }, nil) + // if err != nil { + // return "", err + // } + + accountsClient, err := c.NewStorageAccountsClient() + if err != nil { + return "", err + } + + keys, err := accountsClient.ListKeys(ctx, c.resourceGroupName, c.StorageAccount, nil) + if err != nil { + return "", err + } + + keyCred, err := azblob.NewSharedKeyCredential(c.StorageAccount, *keys.Keys[0].Value) + if err != nil { + return "", err + } + + // Create SAS + perms := sas.BlobPermissions{Create: true, Write: true} + sasQueryParams, err := sas.BlobSignatureValues{ + BlobName: blobName, + ContainerName: c.BlobContainerName, + ExpiryTime: expiry, + Permissions: perms.String(), + Protocol: sas.ProtocolHTTPS, + StartTime: now, + }.SignWithSharedKey(keyCred) + if err != nil { + return "", err + } + + serviceURL := fmt.Sprintf("https://%s.blob.core.windows.net/", c.StorageAccount) + sasURL := fmt.Sprintf("%s%s/%s?%s", serviceURL, c.BlobContainerName, url.PathEscape(blobName), sasQueryParams.Encode()) + return sasURL, nil +} diff --git a/src/pkg/clouds/azure/aci/upload_test.go b/src/pkg/clouds/azure/aci/upload_test.go new file mode 100644 index 000000000..575af2ad2 --- /dev/null +++ b/src/pkg/clouds/azure/aci/upload_test.go @@ -0,0 +1,43 @@ +//go:integration + +package aci + +import ( + "context" + "io" + "strings" + "testing" + + "github.com/DefangLabs/defang/src/pkg/http" +) + +func TestCreateUploadURL(t *testing.T) { + // Create a new container instance + container := NewContainerInstance("defang-cd", "westeurope") + + // Call the CreateUploadURL method + url, err := container.CreateUploadURL(context.Background(), "sha256-Jv4+base64/encoded_digest=") + if err != nil { + t.Fatalf("failed to create upload URL: %v", err) + } + + // Verify the URL is not empty + if url == "" { + t.Fatal("expected non-empty upload URL") + } + + t.Logf("Upload URL: %s", url) + header := http.Header{"Content-Type": []string{"application/text"}} + header.Set("X-Ms-Blob-Type", "BlockBlob") + resp, err := http.PutWithHeader(context.Background(), url, header, strings.NewReader("test content")) + if err != nil { + t.Fatalf("failed to upload content: %v", err) + } + defer resp.Body.Close() + + // Verify the response + if resp.StatusCode != 201 { + body, _ := io.ReadAll(resp.Body) + t.Fatalf("expected status OK, got %v: %s", resp.Status, body) + } +} diff --git a/src/pkg/clouds/azure/common.go b/src/pkg/clouds/azure/common.go index b13b9158f..5bdf0d7aa 100644 --- a/src/pkg/clouds/azure/common.go +++ b/src/pkg/clouds/azure/common.go @@ -40,7 +40,7 @@ func (a Azure) NewStorageAccountsClient() (*armstorage.AccountsClient, error) { return clientFactory.NewAccountsClient(), nil } -func (a Azure) NewStorageClient() (*armstorage.BlobContainersClient, error) { +func (a Azure) NewBlobContainersClient() (*armstorage.BlobContainersClient, error) { cred, err := a.NewCreds() if err != nil { return nil, err @@ -53,3 +53,17 @@ func (a Azure) NewStorageClient() (*armstorage.BlobContainersClient, error) { return clientFactory.NewBlobContainersClient(), nil } + +// func (a Azure) NewRoleAssignmentsClient() (*armauthorization.RoleAssignmentsClient, error) { +// cred, err := a.NewCreds() +// if err != nil { +// return nil, err +// } + +// clientFactory, err := armauthorization.NewRoleAssignmentsClient(a.SubscriptionID, cred, nil) +// if err != nil { +// return nil, fmt.Errorf("failed to create role assignments client: %w", err) +// } + +// return clientFactory, nil +// } diff --git a/src/pkg/http/post.go b/src/pkg/http/post.go index 9740dfb37..67fae830d 100644 --- a/src/pkg/http/post.go +++ b/src/pkg/http/post.go @@ -36,11 +36,15 @@ func PostFormWithContext(ctx context.Context, url string, data url.Values) (*htt return PostWithContext(ctx, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } -func PostWithContext(ctx context.Context, url, contentType string, body io.Reader) (*http.Response, error) { +func PostWithHeader(ctx context.Context, url string, header http.Header, body io.Reader) (*http.Response, error) { hreq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) if err != nil { return nil, err } - hreq.Header.Set("Content-Type", contentType) + hreq.Header = header return DefaultClient.Do(hreq) } + +func PostWithContext(ctx context.Context, url, contentType string, body io.Reader) (*http.Response, error) { + return PostWithHeader(ctx, url, http.Header{"Content-Type": []string{contentType}}, body) +} diff --git a/src/pkg/http/put.go b/src/pkg/http/put.go index 8304b72b4..7048863d5 100644 --- a/src/pkg/http/put.go +++ b/src/pkg/http/put.go @@ -18,10 +18,14 @@ import ( // See the Client.Do method documentation for details on how redirects // are handled. func Put(ctx context.Context, url string, contentType string, body io.Reader) (*http.Response, error) { + return PutWithHeader(ctx, url, http.Header{"Content-Type": []string{contentType}}, body) +} + +func PutWithHeader(ctx context.Context, url string, header http.Header, body io.Reader) (*http.Response, error) { req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, body) if err != nil { return nil, err } - req.Header.Set("Content-Type", contentType) + req.Header = header return DefaultClient.Do(req) } From 52aa941c4af16c12074cacf4a6c48584da5f36c9 Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Thu, 7 Aug 2025 17:31:11 -0700 Subject: [PATCH 6/7] Use SAS for build context --- src/pkg/cli/client/byoc/aws/byoc.go | 2 + src/pkg/cli/client/byoc/azure/byoc.go | 89 ++++++++++++++++++++++++--- src/pkg/cli/client/byoc/gcp/byoc.go | 1 + src/pkg/cli/compose/context.go | 11 ++-- src/pkg/clouds/azure/aci/common.go | 4 +- src/pkg/clouds/azure/aci/setup.go | 4 ++ src/pkg/clouds/azure/aci/upload.go | 25 +++++--- 7 files changed, 108 insertions(+), 28 deletions(-) diff --git a/src/pkg/cli/client/byoc/aws/byoc.go b/src/pkg/cli/client/byoc/aws/byoc.go index a54bebe8d..9d87f8ec4 100644 --- a/src/pkg/cli/client/byoc/aws/byoc.go +++ b/src/pkg/cli/client/byoc/aws/byoc.go @@ -372,6 +372,8 @@ func (b *ByocAws) bucketName() string { func (b *ByocAws) environment(projectName string) (map[string]string, error) { region := b.driver.Region // TODO: this should be the destination region, not the CD region; make customizable + + // From https://www.pulumi.com/docs/iac/concepts/state-and-backends/#aws-s3 defangStateUrl := fmt.Sprintf(`s3://%s?region=%s&awssdk=v2`, b.bucketName(), region) pulumiBackendKey, pulumiBackendValue, err := byoc.GetPulumiBackend(defangStateUrl) if err != nil { diff --git a/src/pkg/cli/client/byoc/azure/byoc.go b/src/pkg/cli/client/byoc/azure/byoc.go index ea9f7b9bc..9af9e6fb7 100644 --- a/src/pkg/cli/client/byoc/azure/byoc.go +++ b/src/pkg/cli/client/byoc/azure/byoc.go @@ -2,13 +2,20 @@ package azure import ( "context" + "encoding/base64" "errors" + "fmt" + "os" + "github.com/DefangLabs/defang/src/pkg" "github.com/DefangLabs/defang/src/pkg/cli/client" "github.com/DefangLabs/defang/src/pkg/cli/client/byoc" + "github.com/DefangLabs/defang/src/pkg/cli/compose" "github.com/DefangLabs/defang/src/pkg/clouds/azure/aci" + "github.com/DefangLabs/defang/src/pkg/term" "github.com/DefangLabs/defang/src/pkg/types" defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1" + "google.golang.org/protobuf/proto" ) type ByocAzure struct { @@ -48,6 +55,10 @@ func (b *ByocAzure) BootstrapList(context.Context) ([]string, error) { // CreateUploadURL implements client.Provider. func (b *ByocAzure) CreateUploadURL(ctx context.Context, req *defangv1.UploadURLRequest) (*defangv1.UploadURLResponse, error) { + if err := b.setUp(ctx); err != nil { + return nil, err + } + url, err := b.driver.CreateUploadURL(ctx, req.Digest) if err != nil { return nil, err @@ -68,10 +79,74 @@ func (b *ByocAzure) DeleteConfig(context.Context, *defangv1.Secrets) error { return errors.ErrUnsupported } +func (b *ByocAzure) setUp(ctx context.Context) error { + return b.driver.SetUp(ctx, []types.Container{ + { + Name: "defang-cd", + Image: b.CDImage, + }, + }) +} + // Deploy implements client.Provider. func (b *ByocAzure) Deploy(ctx context.Context, req *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { - // return nil, errors.ErrUnsupported - return &defangv1.DeployResponse{}, byoc.DebugPulumi(ctx, nil, "up", "payload") + return b.deploy(ctx, req, "up") +} + +func (b *ByocAzure) deploy(ctx context.Context, req *defangv1.DeployRequest, verb string) (*defangv1.DeployResponse, error) { + // If multiple Compose files were provided, req.Compose is the merged representation of all the files + project, err := compose.LoadFromContent(ctx, req.Compose, "") + if err != nil { + return nil, err + } + + if err := b.setUp(ctx); err != nil { + return nil, err + } + + etag := pkg.RandomID() + serviceInfos, err := b.GetServiceInfos(ctx, project.Name, req.DelegateDomain, etag, project.Services) + if err != nil { + return nil, err + } + + data, err := proto.Marshal(&defangv1.ProjectUpdate{ + CdVersion: b.CDImage, + Compose: req.Compose, + Services: serviceInfos, + }) + if err != nil { + return nil, err + } + + // From https://www.pulumi.com/docs/iac/concepts/state-and-backends/#azure-blob-storage + defangStateUrl := fmt.Sprintf(`azblob://%s?storage_account=%s`, b.driver.BlobContainerName, b.driver.StorageAccount) + pulumiBackendKey, pulumiBackendValue, err := byoc.GetPulumiBackend(defangStateUrl) + if err != nil { + return nil, err + } + env := []string{ + "AZURE_LOCATION=" + b.driver.Location.String(), + "AZURE_SUBSCRIPTION_ID=" + b.driver.SubscriptionID, + "DEFANG_DEBUG=" + os.Getenv("DEFANG_DEBUG"), // TODO: use the global DoDebug flag + "DEFANG_JSON=" + os.Getenv("DEFANG_JSON"), + "DEFANG_ORG=" + b.TenantName, + "DEFANG_PREFIX=" + byoc.DefangPrefix, + "DEFANG_STATE_URL=" + defangStateUrl, + "NPM_CONFIG_UPDATE_NOTIFIER=" + "false", + // "PRIVATE_DOMAIN=" + byoc.GetPrivateDomain(projectName), TODO: implement + "PROJECT=" + project.Name, // may be empty + pulumiBackendKey + "=" + pulumiBackendValue, // TODO: make secret + "PULUMI_CONFIG_PASSPHRASE=" + byoc.PulumiConfigPassphrase, // TODO: make secret + "PULUMI_COPILOT=false", + "PULUMI_SKIP_UPDATE_CHECK=true", + "STACK=" + b.PulumiStack, + } + if !term.StdoutCanColor() { + env = append(env, "NO_COLOR=1") + } + err = byoc.DebugPulumi(ctx, env, verb, base64.StdEncoding.EncodeToString(data)) // TODO: handle large projects + return &defangv1.DeployResponse{Etag: etag, Services: serviceInfos}, err } // Destroy implements client.Provider. @@ -111,8 +186,8 @@ func (b *ByocAzure) PrepareDomainDelegation(context.Context, client.PrepareDomai } // Preview implements client.Provider. -func (b *ByocAzure) Preview(context.Context, *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { - return nil, errors.ErrUnsupported +func (b *ByocAzure) Preview(ctx context.Context, req *defangv1.DeployRequest) (*defangv1.DeployResponse, error) { + return b.deploy(ctx, req, "preview") } // PutConfig implements client.Provider. @@ -142,12 +217,6 @@ func (b *ByocAzure) ServiceDNS(host string) string { return host } -// SetCanIUseConfig implements client.Provider. -// Subtle: this method shadows the method (*ByocBaseClient).SetCanIUseConfig of ByocAzure.ByocBaseClient. -func (b *ByocAzure) SetCanIUseConfig(*defangv1.CanIUseResponse) { - // return nil, errors.ErrUnsupported -} - // Subscribe implements client.Provider. func (b *ByocAzure) Subscribe(context.Context, *defangv1.SubscribeRequest) (client.ServerStream[defangv1.SubscribeResponse], error) { return nil, errors.ErrUnsupported diff --git a/src/pkg/cli/client/byoc/gcp/byoc.go b/src/pkg/cli/client/byoc/gcp/byoc.go index 87a2466be..737ee5292 100644 --- a/src/pkg/cli/client/byoc/gcp/byoc.go +++ b/src/pkg/cli/client/byoc/gcp/byoc.go @@ -317,6 +317,7 @@ type cdCommand struct { } func (b *ByocGcp) runCdCommand(ctx context.Context, cmd cdCommand) (string, error) { + // From https://www.pulumi.com/docs/iac/concepts/state-and-backends/#google-cloud-storage defangStateUrl := `gs://` + b.bucket pulumiBackendKey, pulumiBackendValue, err := byoc.GetPulumiBackend(defangStateUrl) if err != nil { diff --git a/src/pkg/cli/compose/context.go b/src/pkg/cli/compose/context.go index b07acf9be..b6a2a80fb 100644 --- a/src/pkg/cli/compose/context.go +++ b/src/pkg/cli/compose/context.go @@ -233,7 +233,7 @@ func uploadArchive(ctx context.Context, provider client.Provider, project string // Do an HTTP PUT to the generated URL header := http.Header{"Content-Type": []string{string(contentType)}} - header.Set("X-Ms-Blob-Type", "BlockBlob") // HACK: move to provider + header.Set("X-Ms-Blob-Type", "BlockBlob") // HACK: move to Azure provider resp, err := http.PutWithHeader(ctx, res.Url, header, body) if err != nil { return "", err @@ -243,12 +243,11 @@ func uploadArchive(ctx context.Context, provider client.Provider, project string return "", fmt.Errorf("HTTP PUT failed with status code %v", resp.Status) } - url := http.RemoveQueryParam(res.Url) // remove any access signature + url := res.Url //http.RemoveQueryParam(res.Url) // remove any access signature + + const gcpPrefix = "https://storage.googleapis.com/" // HACK: move to GCP provider + url = strings.Replace(url, gcpPrefix, "gs://", 1) - const gcpPrefix = "https://storage.googleapis.com/" - if strings.HasPrefix(url, gcpPrefix) { - url = "gs://" + url[len(gcpPrefix):] - } return url, nil } diff --git a/src/pkg/clouds/azure/aci/common.go b/src/pkg/clouds/azure/aci/common.go index c7239dd83..7b813ee8c 100644 --- a/src/pkg/clouds/azure/aci/common.go +++ b/src/pkg/clouds/azure/aci/common.go @@ -17,7 +17,7 @@ type ContainerInstance struct { BlobContainerName string } -func NewContainerInstance(resourceGroupName string, location azure.Location) *ContainerInstance { +func NewContainerInstance(resourceGroupPrefix string, location azure.Location) *ContainerInstance { if location == "" { location = azure.Location(os.Getenv("AZURE_LOCATION")) } @@ -26,7 +26,7 @@ func NewContainerInstance(resourceGroupName string, location azure.Location) *Co Location: location, SubscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"), }, - resourceGroupName: resourceGroupName, // TODO: append location? + resourceGroupName: resourceGroupPrefix + location.String(), StorageAccount: os.Getenv("DEFANG_CD_BUCKET"), } } diff --git a/src/pkg/clouds/azure/aci/setup.go b/src/pkg/clouds/azure/aci/setup.go index a1a4d99b4..07dcf92fc 100644 --- a/src/pkg/clouds/azure/aci/setup.go +++ b/src/pkg/clouds/azure/aci/setup.go @@ -105,6 +105,10 @@ func (c *ContainerInstance) getStorageAccount(ctx context.Context, accountsClien return c.StorageAccount, nil } + if sa := os.Getenv("AZURE_STORAGE_ACCOUNT"); sa != "" { + return sa, nil + } + for pager := accountsClient.NewListByResourceGroupPager(c.resourceGroupName, nil); pager.More(); { page, err := pager.NextPage(ctx) if err != nil { diff --git a/src/pkg/clouds/azure/aci/upload.go b/src/pkg/clouds/azure/aci/upload.go index 576df0927..f6749d635 100644 --- a/src/pkg/clouds/azure/aci/upload.go +++ b/src/pkg/clouds/azure/aci/upload.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/url" + "os" "strings" "time" @@ -40,23 +41,27 @@ func (c *ContainerInstance) CreateUploadURL(ctx context.Context, blobName string // return "", err // } - accountsClient, err := c.NewStorageAccountsClient() - if err != nil { - return "", err - } + storageKey := os.Getenv("AZURE_STORAGE_KEY") + if storageKey == "" { + accountsClient, err := c.NewStorageAccountsClient() + if err != nil { + return "", err + } - keys, err := accountsClient.ListKeys(ctx, c.resourceGroupName, c.StorageAccount, nil) - if err != nil { - return "", err + keys, err := accountsClient.ListKeys(ctx, c.resourceGroupName, c.StorageAccount, nil) + if err != nil { + return "", err + } + storageKey = *keys.Keys[0].Value // or [1]? } - keyCred, err := azblob.NewSharedKeyCredential(c.StorageAccount, *keys.Keys[0].Value) + keyCred, err := azblob.NewSharedKeyCredential(c.StorageAccount, storageKey) if err != nil { return "", err } - // Create SAS - perms := sas.BlobPermissions{Create: true, Write: true} + // Create SAS; TODO: how does AZURE_STORAGE_SAS_TOKEN env var work? + perms := sas.BlobPermissions{Create: true, Write: true, Read: true} // read is for ACR sasQueryParams, err := sas.BlobSignatureValues{ BlobName: blobName, ContainerName: c.BlobContainerName, From 4303f23fcc76bf4fd6c96c0ca0bf53fc846bb3f5 Mon Sep 17 00:00:00 2001 From: Lionello Lunesu Date: Wed, 13 Aug 2025 09:56:34 -0700 Subject: [PATCH 7/7] fix go build tags --- src/pkg/clouds/azure/aci/common_test.go | 3 +++ src/pkg/clouds/azure/aci/run_test.go | 2 +- src/pkg/clouds/azure/aci/setup_test.go | 5 +---- src/pkg/clouds/azure/aci/tail_test.go | 2 +- src/pkg/clouds/azure/aci/upload_test.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pkg/clouds/azure/aci/common_test.go b/src/pkg/clouds/azure/aci/common_test.go index e0935f021..8d1db7b3b 100644 --- a/src/pkg/clouds/azure/aci/common_test.go +++ b/src/pkg/clouds/azure/aci/common_test.go @@ -3,9 +3,12 @@ package aci import ( "testing" + "github.com/DefangLabs/defang/src/pkg" "github.com/google/uuid" ) +var testResourceGroupName = "crun-test-" + pkg.GetCurrentUser() // avoid conflict with other users in the same account + func TestNewClient(t *testing.T) { t.Setenv("AZURE_SUBSCRIPTION_ID", uuid.NewString()) diff --git a/src/pkg/clouds/azure/aci/run_test.go b/src/pkg/clouds/azure/aci/run_test.go index c32d50839..adcf6985f 100644 --- a/src/pkg/clouds/azure/aci/run_test.go +++ b/src/pkg/clouds/azure/aci/run_test.go @@ -1,4 +1,4 @@ -//go:integration +//go:build integration package aci diff --git a/src/pkg/clouds/azure/aci/setup_test.go b/src/pkg/clouds/azure/aci/setup_test.go index 7eef79fad..c7ae0ac7b 100644 --- a/src/pkg/clouds/azure/aci/setup_test.go +++ b/src/pkg/clouds/azure/aci/setup_test.go @@ -1,4 +1,4 @@ -//go:integration +//go:build integration package aci @@ -6,12 +6,9 @@ import ( "context" "testing" - "github.com/DefangLabs/defang/src/pkg" "github.com/DefangLabs/defang/src/pkg/types" ) -var testResourceGroupName = "crun-test-" + pkg.GetCurrentUser() // avoid conflict with other users in the same account - func TestSetup(t *testing.T) { if testing.Short() { t.Skip("skipping slow integration test in short mode") diff --git a/src/pkg/clouds/azure/aci/tail_test.go b/src/pkg/clouds/azure/aci/tail_test.go index cfd285282..0f6170c5c 100644 --- a/src/pkg/clouds/azure/aci/tail_test.go +++ b/src/pkg/clouds/azure/aci/tail_test.go @@ -1,4 +1,4 @@ -//go:integration +//go:build integration package aci diff --git a/src/pkg/clouds/azure/aci/upload_test.go b/src/pkg/clouds/azure/aci/upload_test.go index 575af2ad2..40154810e 100644 --- a/src/pkg/clouds/azure/aci/upload_test.go +++ b/src/pkg/clouds/azure/aci/upload_test.go @@ -1,4 +1,4 @@ -//go:integration +//go:build integration package aci