diff --git a/.golangci.yml b/.golangci.yml index 60fbd37bcb4..3d0ae009a48 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -150,7 +150,7 @@ linters-settings: alias: $1 - pkg: "^github\\.com/Azure/ARO-RP/pkg/client/services/redhatopenshift/mgmt/([0-9]+)-([0-9]+)-([0-9]+)-?(preview)?/redhatopenshift$" alias: mgmtredhatopenshift$1$2$3$4 - - pkg: "^github\\.com/Azure/ARO-RP/pkg/(dbtoken|deploy|gateway|mirror|monitor|operator|portal)$" + - pkg: "^github\\.com/Azure/ARO-RP/pkg/(deploy|gateway|mirror|monitor|operator|portal)$" alias: pkg$1 - pkg: "^github\\.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/redhatopenshift/([0-9]+)-([0-9]+)-([0-9]+)-?(preview)?/redhatopenshift$" alias: redhatopenshift$1$2$3$4 diff --git a/Dockerfile.aro-multistage b/Dockerfile.aro-multistage index 3edac6cdb53..4202ddf5f94 100644 --- a/Dockerfile.aro-multistage +++ b/Dockerfile.aro-multistage @@ -15,5 +15,5 @@ FROM ${REGISTRY}/ubi8/ubi-minimal RUN microdnf update && microdnf clean all COPY --from=builder /app/aro /app/e2e.test /usr/local/bin/ ENTRYPOINT ["aro"] -EXPOSE 2222/tcp 8080/tcp 8443/tcp 8444/tcp 8445/tcp +EXPOSE 2222/tcp 8080/tcp 8443/tcp 8444/tcp USER 1000 diff --git a/Dockerfile.ci-rp b/Dockerfile.ci-rp index 69c06f318d7..949f33764c8 100644 --- a/Dockerfile.ci-rp +++ b/Dockerfile.ci-rp @@ -67,5 +67,5 @@ LABEL aro-final=true RUN microdnf update && microdnf clean all COPY --from=builder /app/aro /app/e2e.test /usr/local/bin/ ENTRYPOINT ["aro"] -EXPOSE 2222/tcp 8080/tcp 8443/tcp 8444/tcp 8445/tcp +EXPOSE 2222/tcp 8080/tcp 8443/tcp 8444/tcp USER 1000 diff --git a/cmd/aro/const.go b/cmd/aro/const.go index 65d924c316d..a851a055801 100644 --- a/cmd/aro/const.go +++ b/cmd/aro/const.go @@ -7,7 +7,6 @@ const ( envDatabaseName = "DATABASE_NAME" envDatabaseAccountName = "DATABASE_ACCOUNT_NAME" envKeyVaultPrefix = "KEYVAULT_PREFIX" - envDBTokenUrl = "DBTOKEN_URL" envOpenShiftVersions = "OPENSHIFT_VERSIONS" envInstallerImageDigests = "INSTALLER_IMAGE_DIGESTS" ) diff --git a/cmd/aro/dbtoken.go b/cmd/aro/dbtoken.go deleted file mode 100644 index 20264afea00..00000000000 --- a/cmd/aro/dbtoken.go +++ /dev/null @@ -1,130 +0,0 @@ -package main - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "net" - "os" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" - "github.com/sirupsen/logrus" - - "github.com/Azure/ARO-RP/pkg/database" - "github.com/Azure/ARO-RP/pkg/database/cosmosdb" - pkgdbtoken "github.com/Azure/ARO-RP/pkg/dbtoken" - "github.com/Azure/ARO-RP/pkg/env" - "github.com/Azure/ARO-RP/pkg/metrics/statsd" - "github.com/Azure/ARO-RP/pkg/metrics/statsd/golang" - "github.com/Azure/ARO-RP/pkg/util/keyvault" - "github.com/Azure/ARO-RP/pkg/util/oidc" -) - -func dbtoken(ctx context.Context, log *logrus.Entry) error { - _env, err := env.NewCore(ctx, log, env.COMPONENT_DBTOKEN) - if err != nil { - return err - } - - if err := env.ValidateVars("AZURE_GATEWAY_SERVICE_PRINCIPAL_ID", "AZURE_DBTOKEN_CLIENT_ID"); err != nil { - return err - } - - if !_env.IsLocalDevelopmentMode() { - if err := env.ValidateVars("MDM_ACCOUNT", "MDM_NAMESPACE"); err != nil { - return err - } - } - - msiToken, err := _env.NewMSITokenCredential() - if err != nil { - return err - } - - msiKVAuthorizer, err := _env.NewMSIAuthorizer(_env.Environment().KeyVaultScope) - if err != nil { - return err - } - - m := statsd.New(ctx, log.WithField("component", "dbtoken"), _env, os.Getenv("MDM_ACCOUNT"), os.Getenv("MDM_NAMESPACE"), os.Getenv("MDM_STATSD_SOCKET")) - - g, err := golang.NewMetrics(log.WithField("component", "dbtoken"), m) - if err != nil { - return err - } - - go g.Run() - - if err := env.ValidateVars(envDatabaseAccountName); err != nil { - return err - } - - dbAccountName := os.Getenv(envDatabaseAccountName) - - clientOptions := &policy.ClientOptions{ - ClientOptions: _env.Environment().ManagedIdentityCredentialOptions().ClientOptions, - } - logrusEntry := log.WithField("component", "database") - dbAuthorizer, err := database.NewMasterKeyAuthorizer(ctx, logrusEntry, msiToken, clientOptions, _env.SubscriptionID(), _env.ResourceGroup(), dbAccountName) - if err != nil { - return err - } - - dbc, err := database.NewDatabaseClient(log.WithField("component", "database"), _env, dbAuthorizer, m, nil, dbAccountName) - if err != nil { - return err - } - - dbName, err := DBName(_env.IsLocalDevelopmentMode()) - if err != nil { - return err - } - - userc := cosmosdb.NewUserClient(dbc, dbName) - - err = pkgdbtoken.ConfigurePermissions(ctx, dbName, userc) - if err != nil { - return err - } - - if err := env.ValidateVars(envKeyVaultPrefix); err != nil { - return err - } - keyVaultPrefix := os.Getenv(envKeyVaultPrefix) - dbtokenKeyvaultURI := keyvault.URI(_env, env.DBTokenKeyvaultSuffix, keyVaultPrefix) - dbtokenKeyvault := keyvault.NewManager(msiKVAuthorizer, dbtokenKeyvaultURI) - - servingKey, servingCerts, err := dbtokenKeyvault.GetCertificateSecret(ctx, env.DBTokenServerSecretName) - if err != nil { - return err - } - - // example value: https://login.microsoftonline.com/11111111-1111-1111-1111-111111111111/v2.0 - issuer := _env.Environment().ActiveDirectoryEndpoint + _env.TenantID() + "/v2.0" - clientID := os.Getenv("AZURE_DBTOKEN_CLIENT_ID") - - verifier, err := oidc.NewVerifier(ctx, issuer, clientID) - if err != nil { - return err - } - - address := "localhost:8445" - if !_env.IsLocalDevelopmentMode() { - address = ":8445" - } - - l, err := net.Listen("tcp", address) - if err != nil { - return err - } - - log.Print("listening") - - server, err := pkgdbtoken.NewServer(ctx, _env, log.WithField("component", "dbtoken"), log.WithField("component", "dbtoken-access"), l, servingKey, servingCerts, verifier, userc, m) - if err != nil { - return err - } - - return server.Run(ctx) -} diff --git a/cmd/aro/gateway.go b/cmd/aro/gateway.go index 49956b65b25..d64a31ced12 100644 --- a/cmd/aro/gateway.go +++ b/cmd/aro/gateway.go @@ -5,16 +5,14 @@ package main import ( "context" + "fmt" "os" "os/signal" - "strings" "syscall" - "time" "github.com/sirupsen/logrus" "github.com/Azure/ARO-RP/pkg/database" - pkgdbtoken "github.com/Azure/ARO-RP/pkg/dbtoken" "github.com/Azure/ARO-RP/pkg/env" pkggateway "github.com/Azure/ARO-RP/pkg/gateway" "github.com/Azure/ARO-RP/pkg/metrics/statsd" @@ -28,10 +26,6 @@ func gateway(ctx context.Context, log *logrus.Entry) error { return err } - if err = env.ValidateVars("AZURE_DBTOKEN_CLIENT_ID"); err != nil { - return err - } - m := statsd.New(ctx, log.WithField("component", "gateway"), _env, os.Getenv("MDM_ACCOUNT"), os.Getenv("MDM_NAMESPACE"), os.Getenv("MDM_STATSD_SOCKET")) g, err := golang.NewMetrics(log.WithField("component", "gateway"), m) @@ -44,36 +38,23 @@ func gateway(ctx context.Context, log *logrus.Entry) error { if err := env.ValidateVars(envDatabaseAccountName); err != nil { return err } - dbc, err := database.NewDatabaseClient(log.WithField("component", "database"), _env, nil, m, nil, os.Getenv(envDatabaseAccountName)) + + msiToken, err := _env.NewMSITokenCredential() if err != nil { return err } + logrusEntry := log.WithField("component", "database") - // Access token GET request needs to be: - // http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=$AZURE_DBTOKEN_CLIENT_ID - // - // In this context, the "resource" parameter is passed to azidentity as a - // "scope" argument even though a scope normally consists of an endpoint URL. - scope := os.Getenv("AZURE_DBTOKEN_CLIENT_ID") - msiRefresherAuthorizer, err := _env.NewMSIAuthorizer(scope) + dbAccountName := os.Getenv(envDatabaseAccountName) + scope := []string{fmt.Sprintf("https://%s.%s", dbAccountName, _env.Environment().CosmosDBDNSSuffixScope)} + dbAuthorizer, err := database.NewTokenAuthorizer(ctx, logrusEntry, msiToken, dbAccountName, scope) if err != nil { return err } - - // TODO: refactor this poor man's feature flag - insecureSkipVerify := _env.IsLocalDevelopmentMode() - for _, feature := range strings.Split(os.Getenv("GATEWAY_FEATURES"), ",") { - if feature == "InsecureSkipVerifyDBTokenCertificate" { - insecureSkipVerify = true - break - } - } - - url, err := getURL(_env.IsLocalDevelopmentMode()) + dbc, err := database.NewDatabaseClient(logrusEntry, _env, dbAuthorizer, m, nil, dbAccountName) if err != nil { return err } - dbRefresher := pkgdbtoken.NewRefresher(log, _env, msiRefresherAuthorizer, insecureSkipVerify, dbc, "gateway", m, "gateway", url) dbName, err := DBName(_env.IsLocalDevelopmentMode()) if err != nil { @@ -85,15 +66,6 @@ func gateway(ctx context.Context, log *logrus.Entry) error { return err } - go func() { - _ = dbRefresher.Run(ctx) - }() - - log.Print("waiting for database token") - for !dbRefresher.HasSyncedOnce() { - time.Sleep(time.Second) - } - httpl, err := utilnet.Listen("tcp", ":8080", pkggateway.SocketSize) if err != nil { return err @@ -130,15 +102,3 @@ func gateway(ctx context.Context, log *logrus.Entry) error { return nil } - -func getURL(isLocalDevelopmentMode bool) (string, error) { - if isLocalDevelopmentMode { - return "https://localhost:8445", nil - } - - if err := env.ValidateVars(envDBTokenUrl); err != nil { - return "", err - } - - return os.Getenv(envDBTokenUrl), nil -} diff --git a/cmd/aro/main.go b/cmd/aro/main.go index 1bdd4b32c4d..0019e0de366 100644 --- a/cmd/aro/main.go +++ b/cmd/aro/main.go @@ -20,7 +20,6 @@ import ( func usage() { fmt.Fprint(flag.CommandLine.Output(), "usage:\n") - fmt.Fprintf(flag.CommandLine.Output(), " %s dbtoken\n", os.Args[0]) fmt.Fprintf(flag.CommandLine.Output(), " %s deploy config.yaml location\n", os.Args[0]) fmt.Fprintf(flag.CommandLine.Output(), " %s gateway\n", os.Args[0]) fmt.Fprintf(flag.CommandLine.Output(), " %s mirror [release_image...]\n", os.Args[0]) @@ -48,9 +47,6 @@ func main() { var err error switch strings.ToLower(flag.Arg(0)) { - case "dbtoken": - checkArgs(1) - err = dbtoken(ctx, log) case "deploy": checkArgs(3) err = deploy(ctx, log) diff --git a/cmd/aro/monitor.go b/cmd/aro/monitor.go index d7ee8133237..58695cde095 100644 --- a/cmd/aro/monitor.go +++ b/cmd/aro/monitor.go @@ -5,9 +5,9 @@ package main import ( "context" + "fmt" "os" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" "github.com/Azure/go-autorest/tracing" "github.com/sirupsen/logrus" kmetrics "k8s.io/client-go/tools/metrics" @@ -88,11 +88,10 @@ func monitor(ctx context.Context, log *logrus.Entry) error { } dbAccountName := os.Getenv(envDatabaseAccountName) - clientOptions := &policy.ClientOptions{ - ClientOptions: _env.Environment().ManagedIdentityCredentialOptions().ClientOptions, - } + logrusEntry := log.WithField("component", "database") - dbAuthorizer, err := database.NewMasterKeyAuthorizer(ctx, logrusEntry, msiToken, clientOptions, _env.SubscriptionID(), _env.ResourceGroup(), dbAccountName) + scope := []string{fmt.Sprintf("https://%s.%s", dbAccountName, _env.Environment().CosmosDBDNSSuffixScope)} + dbAuthorizer, err := database.NewTokenAuthorizer(ctx, logrusEntry, msiToken, dbAccountName, scope) if err != nil { return err } @@ -106,6 +105,7 @@ func monitor(ctx context.Context, log *logrus.Entry) error { if err != nil { return err } + dbMonitors, err := database.NewMonitors(ctx, dbc, dbName) if err != nil { return err diff --git a/cmd/aro/portal.go b/cmd/aro/portal.go index 009a4b219de..eb71913af6f 100644 --- a/cmd/aro/portal.go +++ b/cmd/aro/portal.go @@ -6,11 +6,11 @@ package main import ( "context" "crypto/x509" + "fmt" "net" "os" "strings" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" "github.com/sirupsen/logrus" "github.com/Azure/ARO-RP/pkg/database" @@ -98,11 +98,10 @@ func portal(ctx context.Context, log *logrus.Entry, audit *logrus.Entry) error { } dbAccountName := os.Getenv(envDatabaseAccountName) - clientOptions := &policy.ClientOptions{ - ClientOptions: _env.Environment().ManagedIdentityCredentialOptions().ClientOptions, - } + logrusEntry := log.WithField("component", "database") - dbAuthorizer, err := database.NewMasterKeyAuthorizer(ctx, logrusEntry, msiToken, clientOptions, _env.SubscriptionID(), _env.ResourceGroup(), dbAccountName) + scope := []string{fmt.Sprintf("https://%s.%s", dbAccountName, _env.Environment().CosmosDBDNSSuffixScope)} + dbAuthorizer, err := database.NewTokenAuthorizer(ctx, logrusEntry, msiToken, dbAccountName, scope) if err != nil { return err } @@ -116,6 +115,7 @@ func portal(ctx context.Context, log *logrus.Entry, audit *logrus.Entry) error { if err != nil { return err } + dbOpenShiftClusters, err := database.NewOpenShiftClusters(ctx, dbc, dbName) if err != nil { return err diff --git a/cmd/aro/rp.go b/cmd/aro/rp.go index 99accccecc6..0477f05d5e5 100644 --- a/cmd/aro/rp.go +++ b/cmd/aro/rp.go @@ -10,7 +10,6 @@ import ( "os/signal" "syscall" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" "github.com/Azure/go-autorest/tracing" "github.com/sirupsen/logrus" kmetrics "k8s.io/client-go/tools/metrics" @@ -108,13 +107,11 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error { if err := env.ValidateVars(envDatabaseAccountName); err != nil { return err } - dbAccountName := os.Getenv(envDatabaseAccountName) - clientOptions := &policy.ClientOptions{ - ClientOptions: _env.Environment().ManagedIdentityCredentialOptions().ClientOptions, - } + logrusEntry := log.WithField("component", "database") - dbAuthorizer, err := database.NewMasterKeyAuthorizer(ctx, logrusEntry, msiToken, clientOptions, _env.SubscriptionID(), _env.ResourceGroup(), dbAccountName) + scope := []string{fmt.Sprintf("https://%s.%s", dbAccountName, _env.Environment().CosmosDBDNSSuffixScope)} + dbAuthorizer, err := database.NewTokenAuthorizer(ctx, logrusEntry, msiToken, dbAccountName, scope) if err != nil { return err } @@ -128,6 +125,7 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error { if err != nil { return err } + dbAsyncOperations, err := database.NewAsyncOperations(ctx, _env.IsLocalDevelopmentMode(), dbc, dbName) if err != nil { return err diff --git a/docs/dbtoken-service.md b/docs/dbtoken-service.md deleted file mode 100644 index 9d1a3410d4c..00000000000 --- a/docs/dbtoken-service.md +++ /dev/null @@ -1,73 +0,0 @@ -# DB token service - -## Introduction - -Cosmos DB access control is described -[here](https://docs.microsoft.com/en-us/azure/cosmos-db/secure-access-to-data). -In brief, there are three options: - -1. use r/w or r/o primary keys, which grant access to the whole database account -2. implement a service which transforms (1) into scoped resource tokens -3. a Microsoft Entra ID RBAC-based model. - -Currently, the RP, monitoring and portal service share the same security -boundary (the RP VM) and use option 1. The dbtoken service, which also runs on -the RP VM, is our implementation of option 2. Option 3 is now in GA and there are plans -[here](https://issues.redhat.com/browse/ARO-5512) to implement it and replace option 1 -and 2. - -The purpose of the dbtoken service at its implementation time is to enable the -gateway component (which handles end-user traffic) to access the service Cosmos -DB without recourse to using root credentials. This provides a level of defence -in depth in the face of an attack on the gateway component. - - -## Workflow - -* An AAD application is manually created at rollout, registering the - https://dbtoken.aro.azure.com resource. - -* The dbtoken service receives POST requests from any client wishing to receive - a scoped resource token at its /token?permission= endpoint. - -* The dbtoken service validates that the POST request includes a valid - AAD-signed bearer JWT for the https://dbtoken.aro.azure.com resource. The - subject UUID is retrieved from the JWT. - -* In the case of the gateway service, the JWT subject UUID is the UUID of the - service principal corresponding to the gateway VMSS MSI. - -* Using its primary key Cosmos DB credential, the dbtoken requests a scoped - resource token for the given user UUID and from Cosmos DB and - proxies it to the caller. - -* Clients may use the dbtoken.Refresher interface to handle regularly refreshing - the resource token and injecting it into the database client used by the rest - of the client codebase. - - -## Setup - -* Create the application and set `requestedAccessTokenVersion` - - ```bash - AZURE_DBTOKEN_CLIENT_ID="$(az ad app create --display-name dbtoken \ - --oauth2-allow-implicit-flow false \ - --query appId \ - -o tsv)" - - OBJ_ID="$(az ad app show --id $AZURE_DBTOKEN_CLIENT_ID --query id)" - - # NOTE: the graph API requires this to be done from a managed machine - az rest --method PATCH \ - --uri https://graph.microsoft.com/v1.0/applications/$OBJ_ID/ \ - --body '{"api":{"requestedAccessTokenVersion": 2}}' - ``` - - * Add the `AZURE_DBTOKEN_CLIENT_ID` to the RP config for the respective environment. - -* The dbtoken service is responsible for creating database users and permissions - - * see the ConfigurePermissions function. - - diff --git a/docs/deploy-full-rp-service-in-dev.md b/docs/deploy-full-rp-service-in-dev.md index 0d06d4cf533..d2b3b7152ec 100644 --- a/docs/deploy-full-rp-service-in-dev.md +++ b/docs/deploy-full-rp-service-in-dev.md @@ -232,10 +232,6 @@ --vault-name "$KEYVAULT_PREFIX-por" \ --name portal-client \ --file secrets/portal-client.pem >/dev/null - az keyvault certificate import \ - --vault-name "$KEYVAULT_PREFIX-dbt" \ - --name dbtoken-server \ - --file secrets/localhost.pem >/dev/null ``` 1. Delete the existing VMSS @@ -398,3 +394,4 @@ ``` > __NOTE:__ The `az aro` CLI extension must be registered in order to run `az aro` commands against a local or tunneled RP. The usual hack script used to create clusters does not work due to keyvault mirroring requirements. The name of the cluster depends on the DNS zone that was created in an earlier step. + diff --git a/docs/keyvaults.md b/docs/keyvaults.md index 0280e205504..de55b4e97d6 100644 --- a/docs/keyvaults.md +++ b/docs/keyvaults.md @@ -18,10 +18,6 @@ Majority of the certificates are configured for auto-renewal to ensure that when - Certificates: - This keyvault contains all cluster `api` and `*.apps` certificates used within OpenShift. These certificates are auto-rotated and pushed to clusters during AdminUpdates in the `configureAPIServerCertificate` and `configureIngressCertificate` steps. These certificates will not be generated if the `DisableSignedCertificates` [feature flag](./feature-flags.md) is set within the RP config. -1. DBToken (dbt) - - Certificates: - - `dbtoken-server` is a TLS certificate used in the [`dbtoken` service](./dbtoken-service.md) for RESTful calls to issue credentials. - 1. Portal (por) - Certificates: - `portal-client` is a certificate which is used within the aro-portal app registration. The subject of this certificate must match that within the `trustedSubjects` section of the app registration manifest within the Azure portal, otherwise callbacks from the Microsoft AAD login service will not function correctly. @@ -48,3 +44,4 @@ Majority of the certificates are configured for auto-renewal to ensure that when - Certificates: - `gwy-mdm` the certificate used for emitting metrics to the Geneva/MDM service - `gwy-mdsd` the certificate used for emitting logs to the Geneva/MDSD service + diff --git a/docs/prepare-a-shared-rp-development-environment.md b/docs/prepare-a-shared-rp-development-environment.md index 7e50da9dbb2..93153e1119f 100644 --- a/docs/prepare-a-shared-rp-development-environment.md +++ b/docs/prepare-a-shared-rp-development-environment.md @@ -1,8 +1,8 @@ # Prepare a shared RP development environment Follow these steps to build a shared RP development environment and secrets -file. A single RP development environment can be shared across multiple -developers and/or CI flows. It may include multiple resource groups in multiple +file. A single RP development environment can be shared across multiple +developers and/or CI flows. It may include multiple resource groups in multiple locations. ## Prerequisites @@ -12,7 +12,7 @@ locations. applications. 1. You will need a publicly resolvable DNS Zone resource in your Azure - subscription. Set PARENT_DOMAIN_NAME and PARENT_DOMAIN_RESOURCEGROUP to the name and + subscription. Set PARENT_DOMAIN_NAME and PARENT_DOMAIN_RESOURCEGROUP to the name and resource group of the DNS Zone resource: ```bash @@ -21,9 +21,9 @@ locations. ``` 1. You will need a storage account in your Azure subscription in which to store - shared development environment secrets. The storage account must contain a - private container named `secrets`. All team members must have `Storage Blob - Data Reader` or `Storage Blob Data Contributor` role on the storage account. + shared development environment secrets. The storage account must contain a + private container named `secrets`. All team members must have `Storage Blob +Data Reader` or `Storage Blob Data Contributor` role on the storage account. Set SECRET_SA_ACCOUNT_NAME to the name of the storage account: ```bash @@ -32,7 +32,7 @@ locations. 1. You will need an AAD object (this could be your AAD user, or an AAD group of which you are a member) which will be able to administer certificates in the - development environment key vault(s). Set ADMIN_OBJECT_ID to the object ID. + development environment key vault(s). Set ADMIN_OBJECT_ID to the object ID. ```bash ADMIN_OBJECT_ID="$(az ad group show -g 'aro-engineering' --query id -o tsv)" @@ -73,12 +73,11 @@ locations. mkdir -p secrets ``` - ## AAD applications 1. Create an AAD application which will fake up the ARM layer: - This application requires client certificate authentication to be enabled. A + This application requires client certificate authentication to be enabled. A suitable key/certificate file can be generated using the following helper utility: @@ -101,11 +100,11 @@ locations. Later this application will be granted: - * `User Access Administrator` on your subscription. + - `User Access Administrator` on your subscription. 1. Create an AAD application which will fake up the first party application. - This application requires client certificate authentication to be enabled. A + This application requires client certificate authentication to be enabled. A suitable key/certificate file can be generated using the following helper utility: @@ -130,9 +129,9 @@ locations. Later this application will be granted: - * `ARO v4 FP Subscription` on your subscription. - * `DNS Zone Contributor` on the DNS zone in RESOURCEGROUP. - * `Network Contributor` on RESOURCEGROUP. + - `ARO v4 FP Subscription` on your subscription. + - `DNS Zone Contributor` on the DNS zone in RESOURCEGROUP. + - `Network Contributor` on RESOURCEGROUP. 1. Create an AAD application which will fake up the RP identity. @@ -150,9 +149,9 @@ locations. Later this application will be granted: - * `Reader` on RESOURCEGROUP. - * `Secrets / Get` on the key vault in RESOURCEGROUP. - * `DocumentDB Account Contributor` on the CosmosDB resource in RESOURCEGROUP. + - `Reader` on RESOURCEGROUP. + - `Secrets / Get` on the key vault in RESOURCEGROUP. + - `DocumentDB Account Contributor` on the CosmosDB resource in RESOURCEGROUP. 1. Create an AAD application which will fake up the gateway identity. @@ -184,21 +183,21 @@ locations. Later this application will be granted: - * `Contributor` on your subscription. - * `User Access Administrator` on your subscription. + - `Contributor` on your subscription. + - `User Access Administrator` on your subscription. You must also manually grant this application the `Microsoft.Graph/Application.ReadWrite.OwnedBy` permission, which requires admin access, in order for AAD applications to be created/deleted on a per-cluster basis. - * Go into the Azure Portal - * Go to Azure Active Directory - * Navigate to the `aro-v4-tooling-shared` app registration page - * Click 'API permissions' in the left side pane - * Click 'Add a permission'. - * Click 'Microsoft Graph' - * Select 'Application permissions' - * Search for 'Application' and select `Application.ReadWrite.OwnedBy` - * Click 'Add permissions' - * This request will need to be approved by a tenant administrator. If you are one, you can click the `Grant admin consent for ` button to the right of the `Add a permission` button on the app page + - Go into the Azure Portal + - Go to Azure Active Directory + - Navigate to the `aro-v4-tooling-shared` app registration page + - Click 'API permissions' in the left side pane + - Click 'Add a permission'. + - Click 'Microsoft Graph' + - Select 'Application permissions' + - Search for 'Application' and select `Application.ReadWrite.OwnedBy` + - Click 'Add permissions' + - This request will need to be approved by a tenant administrator. If you are one, you can click the `Grant admin consent for ` button to the right of the `Add a permission` button on the app page 1. Set up the RP role definitions and subscription role assignments in your Azure subscription. The usage of "uuidgen" for fpRoleDefinitionId is simply there to keep from interfering with any linked resources and to create the role net new. This mimics the RBAC that ARM sets up. With at least `User Access Administrator` permissions on your subscription, do: @@ -217,7 +216,7 @@ locations. 1. Create an AAD application which will fake up the portal client. - This application requires client certificate authentication to be enabled. A + This application requires client certificate authentication to be enabled. A suitable key/certificate file can be generated using the following helper utility: @@ -238,13 +237,9 @@ locations. --cert "$(base64 -w0 /dev/null ``` -1. Create an AAD application which will fake up the dbtoken client. - - See [dbtoken-service.md](./dbtoken-service.md#setup) for details on setup. - ## Certificates -1. Create the VPN CA key/certificate. A suitable key/certificate file can be +1. Create the VPN CA key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -252,7 +247,7 @@ locations. mv vpn-ca.* secrets ``` -1. Create the VPN client key/certificate. A suitable key/certificate file can be +1. Create the VPN client key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -260,7 +255,7 @@ locations. mv vpn-client.* secrets ``` -1. Create the proxy serving key/certificate. A suitable key/certificate file +1. Create the proxy serving key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -268,7 +263,7 @@ locations. mv proxy.* secrets ``` -1. Create the proxy client key/certificate. A suitable key/certificate file can +1. Create the proxy client key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -276,14 +271,14 @@ locations. mv proxy-client.* secrets ``` -1. Create the proxy ssh key/certificate. A suitable key/certificate file can +1. Create the proxy ssh key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash ssh-keygen -f secrets/proxy_id_rsa -N '' ``` -1. Create an RP serving key/certificate. A suitable key/certificate file +1. Create an RP serving key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -291,7 +286,7 @@ locations. mv localhost.* secrets ``` -1. Create the dev CA key/certificate. A suitable key/certificate file can be +1. Create the dev CA key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -299,7 +294,7 @@ locations. mv dev-ca.* secrets ``` -1. Create the dev client key/certificate. A suitable key/certificate file can +1. Create the dev client key/certificate. A suitable key/certificate file can be generated using the following helper utility: ```bash @@ -326,13 +321,12 @@ import_certs_secrets 5. Next, we need to update certificates owned by FP Service Principal. Current configuration in DEV and INT is listed below. You can get the `AAD APP ID` from the `secrets/env` file -Variable | Certificate Client | Subscription Type | AAD App Name | Key Vault Name | -| --- | --- | --- | --- | --- | -| AZURE_FP_CLIENT_ID | firstparty | DEV | aro-v4-fp-shared-dev | v4-eastus-dev-svc | -| AZURE_ARM_CLIENT_ID | arm | DEV | aro-v4-arm-shared-dev | v4-eastus-dev-svc | -| AZURE_PORTAL_CLIENT_ID | portal-client | DEV | aro-v4-portal-shared-dev | v4-eastus-dev-svc | -| AZURE_FP_CLIENT_ID | firstparty | INT | aro-int-sp | aro-int-eastus-svc | - +| Variable | Certificate Client | Subscription Type | AAD App Name | Key Vault Name | +| ---------------------- | ------------------ | ----------------- | ------------------------ | ------------------ | +| AZURE_FP_CLIENT_ID | firstparty | DEV | aro-v4-fp-shared-dev | v4-eastus-dev-svc | +| AZURE_ARM_CLIENT_ID | arm | DEV | aro-v4-arm-shared-dev | v4-eastus-dev-svc | +| AZURE_PORTAL_CLIENT_ID | portal-client | DEV | aro-v4-portal-shared-dev | v4-eastus-dev-svc | +| AZURE_FP_CLIENT_ID | firstparty | INT | aro-int-sp | aro-int-eastus-svc | ```bash # Import firstparty.pem to keyvault v4-eastus-svc @@ -355,18 +349,18 @@ az ad app credential reset \ 5. The RP makes API calls to kubernetes cluster via a proxy VMSS agent. For the agent to get the updated certificates, this vm needs to be deleted & redeployed. Proxy VM is currently deployed by the `deploy_env_dev` function in `deploy-shared-env.sh`. It makes use of `env-development.json` 6. Run `[rharosecretsdev|e2earosecrets] make secrets-update` to upload it to your -storage account so other people on your team can access it via `make secrets` + storage account so other people on your team can access it via `make secrets` # Environment file -1. Choose the resource group prefix. The resource group location will be +1. Choose the resource group prefix. The resource group location will be The resource group location will be appended to the prefix to make the resource group name. If a v4-prefixed environment exists in the subscription already, use a unique prefix. ```bash RESOURCEGROUP_PREFIX=v4 ``` -1. Choose the proxy domain name label. This final proxy hostname will be of the +1. Choose the proxy domain name label. This final proxy hostname will be of the form `vm0.$PROXY_DOMAIN_NAME_LABEL.$LOCATION.cloudapp.azure.com`. ```bash @@ -382,7 +376,6 @@ storage account so other people on your team can access it via `make secrets` export AZURE_ARM_CLIENT_ID='$AZURE_ARM_CLIENT_ID' export AZURE_FP_CLIENT_ID='$AZURE_FP_CLIENT_ID' export AZURE_FP_SERVICE_PRINCIPAL_ID='$(az ad sp list --filter "appId eq '$AZURE_FP_CLIENT_ID'" --query '[].id' -o tsv)' - export AZURE_DBTOKEN_CLIENT_ID='$AZURE_DBTOKEN_CLIENT_ID' export AZURE_PORTAL_CLIENT_ID='$AZURE_PORTAL_CLIENT_ID' export AZURE_PORTAL_ACCESS_GROUP_IDS='$ADMIN_OBJECT_ID' export AZURE_PORTAL_ELEVATED_GROUP_IDS='$ADMIN_OBJECT_ID' @@ -418,7 +411,7 @@ storage account so other people on your team can access it via `make secrets`. Look at the [helper file](../hack/devtools/deploy-shared-env.sh) to understand each of the bash functions below. -1. Copy, edit (if necessary) and source your environment file. The required +1. Copy, edit (if necessary) and source your environment file. The required environment variable configuration is documented immediately below: ```bash @@ -427,7 +420,7 @@ each of the bash functions below. . ./env ``` - * LOCATION: Location of the shared RP development environment (default: + - LOCATION: Location of the shared RP development environment (default: `eastus`). 1. Create the resource group and deploy the RP resources: @@ -450,7 +443,7 @@ each of the bash functions below. If you encounter a "VirtualNetworkGatewayCannotUseStandardPublicIP" error when running the `deploy_env_dev` command, you have to override two - additional parameters. Run this command instead: + additional parameters. Run this command instead: ```bash deploy_env_dev_override @@ -476,10 +469,10 @@ each of the bash functions below. import_certs_secrets ``` - > __NOTE:__: in production, three additional keys/certificates (rp-mdm, rp-mdsd, and - cluster-mdsd) are also required in the $KEYVAULT_PREFIX-svc key vault. These - are client certificates for RP metric and log forwarding (respectively) to - Geneva. + > **NOTE:**: in production, three additional keys/certificates (rp-mdm, rp-mdsd, and + > cluster-mdsd) are also required in the $KEYVAULT_PREFIX-svc key vault. These + > are client certificates for RP metric and log forwarding (respectively) to + > Geneva. If you need them in development: @@ -508,15 +501,15 @@ each of the bash functions below. --file secrets/cluster-logging-int.pem ``` - > __NOTE:__: in development, if you don't have valid certs for these, you can just - upload `localhost.pem` as a placeholder for each of these. This will avoid an - error stemming from them not existing, but it will result in logging pods - crash looping in any clusters you make. Additionally, no gateway resources are - created in development so you should not need to execute the cert import statement - for the "-gwy" keyvault. + > **NOTE:**: in development, if you don't have valid certs for these, you can just + > upload `localhost.pem` as a placeholder for each of these. This will avoid an + > error stemming from them not existing, but it will result in logging pods + > crash looping in any clusters you make. Additionally, no gateway resources are + > created in development so you should not need to execute the cert import statement + > for the "-gwy" keyvault. 1. In pre-production (int, e2e) certain certificates are provisioned via keyvault -integration. These should be rotated and generated in the keyvault itself: + integration. These should be rotated and generated in the keyvault itself: ``` Vault Name: "$KEYVAULT_PREFIX-svc" @@ -540,8 +533,7 @@ Development value: secrets/cluster-logging-int.pem vpn_configuration ``` - ## Append Resource Group to Subscription Cleaner DenyList -* We have subscription pruning that takes place routinely and need to add our resource group for the shared rp environment to the `denylist` of the cleaner: - * [https://github.com/Azure/ARO-RP/blob/e918d1b87be53a3b3cdf18b674768a6480fb56b8/hack/clean/clean.go#L29](https://github.com/Azure/ARO-RP/blob/e918d1b87be53a3b3cdf18b674768a6480fb56b8/hack/clean/clean.go#L29) +- We have subscription pruning that takes place routinely and need to add our resource group for the shared rp environment to the `denylist` of the cleaner: + - [https://github.com/Azure/ARO-RP/blob/e918d1b87be53a3b3cdf18b674768a6480fb56b8/hack/clean/clean.go#L29](https://github.com/Azure/ARO-RP/blob/e918d1b87be53a3b3cdf18b674768a6480fb56b8/hack/clean/clean.go#L29) diff --git a/go.mod b/go.mod index a5a34cb8899..1d038f8274d 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/gorilla/sessions v1.2.2 github.com/hashicorp/go-multierror v1.1.1 github.com/itchyny/gojq v0.12.13 - github.com/jewzaam/go-cosmosdb v0.0.0-20240320220716-88298caebe4a + github.com/jewzaam/go-cosmosdb v0.0.0-20240603205015-e096456eff37 github.com/jongio/azidext/go/azidext v0.5.0 github.com/microsoft/kiota-abstractions-go v1.2.0 github.com/microsoft/kiota-http-go v1.0.0 diff --git a/go.sum b/go.sum index a2971170158..3ef7a86bac4 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,8 @@ github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOd github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jewzaam/go-cosmosdb v0.0.0-20240320220716-88298caebe4a h1:x8omH/WFc+AYa/2zTNQCLkpPQDxxpSAiYkCpXqRhtUI= -github.com/jewzaam/go-cosmosdb v0.0.0-20240320220716-88298caebe4a/go.mod h1:hEk8/SoE/NBT9ofV69Gzs98vbGyF155SaYl1p9bxKb8= +github.com/jewzaam/go-cosmosdb v0.0.0-20240603205015-e096456eff37 h1:69P76EErLYvPuva+ITbDvOIaA+KH9153kU8QeGhJijk= +github.com/jewzaam/go-cosmosdb v0.0.0-20240603205015-e096456eff37/go.mod h1:hEk8/SoE/NBT9ofV69Gzs98vbGyF155SaYl1p9bxKb8= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= diff --git a/hack/devtools/deploy-shared-env.sh b/hack/devtools/deploy-shared-env.sh index c8b3520e1b1..4835a6cc231 100644 --- a/hack/devtools/deploy-shared-env.sh +++ b/hack/devtools/deploy-shared-env.sh @@ -126,10 +126,6 @@ import_certs_secrets() { --vault-name "$KEYVAULT_PREFIX-por" \ --name portal-server \ --file secrets/localhost.pem >/dev/null - az keyvault certificate import \ - --vault-name "$KEYVAULT_PREFIX-dbt" \ - --name dbtoken-server \ - --file secrets/localhost.pem >/dev/null az keyvault certificate import \ --vault-name "$KEYVAULT_PREFIX-por" \ --name portal-client \ diff --git a/hack/gendevconfig/gendevconfig.go b/hack/gendevconfig/gendevconfig.go index 06358991e86..322f92df6f5 100644 --- a/hack/gendevconfig/gendevconfig.go +++ b/hack/gendevconfig/gendevconfig.go @@ -19,7 +19,6 @@ func run(ctx context.Context, log *logrus.Entry) error { err := env.ValidateVars( "ADMIN_OBJECT_ID", "AZURE_CLIENT_ID", - "AZURE_DBTOKEN_CLIENT_ID", "AZURE_SERVICE_PRINCIPAL_ID", "AZURE_FP_SERVICE_PRINCIPAL_ID", "AZURE_PORTAL_ACCESS_GROUP_IDS", diff --git a/pkg/database/billing.go b/pkg/database/billing.go index ddc815cf45b..8304bb1f368 100644 --- a/pkg/database/billing.go +++ b/pkg/database/billing.go @@ -6,7 +6,6 @@ package database import ( "context" "fmt" - "net/http" "strings" "github.com/Azure/ARO-RP/pkg/api" @@ -32,49 +31,6 @@ type Billing interface { func NewBilling(ctx context.Context, dbc cosmosdb.DatabaseClient, dbName string) (Billing, error) { collc := cosmosdb.NewCollectionClient(dbc, dbName) - triggers := []*cosmosdb.Trigger{ - { - ID: "setCreationBillingTimeStamp", - TriggerOperation: cosmosdb.TriggerOperationCreate, - TriggerType: cosmosdb.TriggerTypePre, - Body: `function trigger() { - var request = getContext().getRequest(); - var body = request.getBody(); - var date = new Date(); - var now = Math.floor(date.getTime() / 1000); - var billingBody = body["billing"]; - if (!billingBody["creationTime"]) { - billingBody["creationTime"] = now; - } - request.setBody(body); -}`, - }, - { - ID: "setDeletionBillingTimeStamp", - TriggerOperation: cosmosdb.TriggerOperationReplace, - TriggerType: cosmosdb.TriggerTypePre, - Body: `function trigger() { - var request = getContext().getRequest(); - var body = request.getBody(); - var date = new Date(); - var now = Math.floor(date.getTime() / 1000); - var billingBody = body["billing"]; - if (!billingBody["deletionTime"]) { - billingBody["deletionTime"] = now; - } - request.setBody(body); -}`, - }, - } - - triggerc := cosmosdb.NewTriggerClient(collc, collBilling) - for _, trigger := range triggers { - _, err := triggerc.Create(ctx, trigger) - if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusConflict) { - return nil, err - } - } - documentClient := cosmosdb.NewBillingDocumentClient(collc, collBilling) return NewBillingWithProvidedClient(documentClient), nil } diff --git a/pkg/database/cosmosdb/zz_generated_authorizer.go b/pkg/database/cosmosdb/zz_generated_authorizer.go index 64f81e70e7c..a9784f9ff3f 100644 --- a/pkg/database/cosmosdb/zz_generated_authorizer.go +++ b/pkg/database/cosmosdb/zz_generated_authorizer.go @@ -10,18 +10,19 @@ import ( "net/http" "net/url" "strings" + "sync" "time" ) type Authorizer interface { - Authorize(*http.Request, string, string) + Authorize(*http.Request, string, string) error } type masterKeyAuthorizer struct { masterKey []byte } -func (a *masterKeyAuthorizer) Authorize(req *http.Request, resourceType, resourceLink string) { +func (a *masterKeyAuthorizer) Authorize(req *http.Request, resourceType, resourceLink string) error { date := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT") h := hmac.New(sha256.New, a.masterKey) @@ -29,6 +30,8 @@ func (a *masterKeyAuthorizer) Authorize(req *http.Request, resourceType, resourc req.Header.Set("Authorization", url.QueryEscape(fmt.Sprintf("type=master&ver=1.0&sig=%s", base64.StdEncoding.EncodeToString(h.Sum(nil))))) req.Header.Set("x-ms-date", date) + + return nil } func NewMasterKeyAuthorizer(masterKey string) (Authorizer, error) { @@ -41,13 +44,96 @@ func NewMasterKeyAuthorizer(masterKey string) (Authorizer, error) { } type tokenAuthorizer struct { - token string + token string + expiration time.Time + cond *sync.Cond + acquiring bool + lastAttempt time.Time + getToken func() (token string, newExpiration time.Time, err error) +} + +func (a *tokenAuthorizer) Authorize(req *http.Request, resourceType, resourceLink string) error { + token, err := a.acquireToken() + if err != nil { + return err + } + authorizationHeader := fmt.Sprintf("type=aad&ver=1.0&sig=%s", token) + currentTime := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT") + req.Header.Set("Authorization", authorizationHeader) + req.Header.Set("x-ms-date", currentTime) + + return nil } -func (a *tokenAuthorizer) Authorize(req *http.Request, resourceType, resourceLink string) { - req.Header.Set("Authorization", url.QueryEscape(a.token)) +func NewTokenAuthorizer(token string, expiration time.Time, getToken func() (token string, newExpiration time.Time, err error)) Authorizer { + return &tokenAuthorizer{token: token, expiration: expiration, getToken: getToken, cond: sync.NewCond(&sync.Mutex{})} } -func NewTokenAuthorizer(token string) Authorizer { - return &tokenAuthorizer{token: token} +// Get returns the underlying resource. +// If the resource is fresh, no refresh is performed. +func (a *tokenAuthorizer) acquireToken() (string, error) { + // If the resource is expiring within this time window, update it eagerly. + // This allows other goroutines to keep running by using the not-yet-expired + // resource value while one goroutine updates the resource. + const window = 5 * time.Minute // To update the token if 5 mins left to expire + const backoff = 30 * time.Second // Minimum wait time between attempts + now, acquire, expired := time.Now(), false, false + + // acquire exclusive lock + a.cond.L.Lock() + token := a.token + + for { + expired = a.expiration.IsZero() || a.expiration.Before(now) + if expired { + if !a.acquiring { + // This goroutine will acquire the token. + a.acquiring, acquire = true, true + break + } + // Getting here means that this goroutine will wait for the updated token + } else if a.expiration.Add(-window).Before(now) { + // The token is valid but is expiring within the time window(5 mins) + if !a.acquiring && a.lastAttempt.Add(backoff).Before(now) { + // If another goroutine is not acquiring/renewing the token, and none has attempted + // to do so within the last 30 seconds, this goroutine will do it + a.acquiring, acquire = true, true + break + } + // This goroutine will use the existing token value while another updates it + token = a.token + break + } else { + // The token is not close to expiring, this so using its current value + token = a.token + break + } + // If we get here, wait for the new token value to be acquired/updated + a.cond.Wait() + } + a.cond.L.Unlock() // Release the lock so no goroutines are blocked + + var err error + if acquire { + // This goroutine has been selected to acquire/update the token + var expiration time.Time + var newValue string + a.lastAttempt = now + newValue, expiration, err = a.getToken() + + // Atomically, update the shared token's new value & expiration. + a.cond.L.Lock() + if err == nil { + // Update token & expiration, return the new value + token = newValue + a.token, a.expiration = token, expiration + } else if !expired { + // An eager update failed. Discard the error and return the current--still valid--token value + err = nil + } + a.acquiring = false + a.cond.L.Unlock() + a.cond.Broadcast() + } + return token, err } diff --git a/pkg/database/cosmosdb/zz_generated_cosmosdb.go b/pkg/database/cosmosdb/zz_generated_cosmosdb.go index 9b159dab7b0..d4cfcd5f71d 100644 --- a/pkg/database/cosmosdb/zz_generated_cosmosdb.go +++ b/pkg/database/cosmosdb/zz_generated_cosmosdb.go @@ -6,7 +6,7 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "net/textproto" "strconv" @@ -108,7 +108,7 @@ func (c *databaseClient) _do(ctx context.Context, method, path, resourceType, re if err != nil { return nil, err } - req.Body = ioutil.NopCloser(buf) + req.Body = io.NopCloser(buf) req.Header.Set("Content-Type", "application/json") } @@ -118,12 +118,12 @@ func (c *databaseClient) _do(ctx context.Context, method, path, resourceType, re req.Header.Set("x-ms-version", "2018-12-31") - c.mu.RLock() if c.authorizer != nil { - c.authorizer.Authorize(req, resourceType, resourceLink) + err := c.authorizer.Authorize(req, resourceType, resourceLink) + if err != nil { + return nil, err + } } - c.mu.RUnlock() - resp, err := c.hc.Do(req) if err != nil { return nil, err diff --git a/pkg/database/database.go b/pkg/database/database.go index 550e93cb809..c2d00e945c3 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -11,6 +11,7 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" + azcorepolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" sdkcosmos "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos/v2" "github.com/sirupsen/logrus" "github.com/ugorji/go/codec" @@ -72,6 +73,21 @@ func NewMasterKeyAuthorizer(ctx context.Context, log *logrus.Entry, token azcore return cosmosdb.NewMasterKeyAuthorizer(getDatabaseKey(keys, log)) } +func NewTokenAuthorizer(ctx context.Context, log *logrus.Entry, cred azcore.TokenCredential, databaseAccountName string, scopes []string) (cosmosdb.Authorizer, error) { + acquireToken := func() (token string, newExpiration time.Time, err error) { + tk, err := cred.GetToken(ctx, azcorepolicy.TokenRequestOptions{Scopes: scopes}) + if err != nil { + return "", time.Time{}, err + } + return tk.Token, tk.ExpiresOn, nil + } + tk, expiration, err := acquireToken() + if err != nil { + return nil, err + } + return cosmosdb.NewTokenAuthorizer(tk, expiration, acquireToken), nil +} + func getDatabaseKey(keys sdkcosmos.DatabaseAccountsClientListKeysResponse, log *logrus.Entry) string { keyName := "SecondaryMasterKey" log.Infof("Using %s to authenticate with CosmosDB", keyName) diff --git a/pkg/database/monitors.go b/pkg/database/monitors.go index aa06dcdb3b6..6d9246def88 100644 --- a/pkg/database/monitors.go +++ b/pkg/database/monitors.go @@ -33,29 +33,6 @@ type Monitors interface { func NewMonitors(ctx context.Context, dbc cosmosdb.DatabaseClient, dbName string) (Monitors, error) { collc := cosmosdb.NewCollectionClient(dbc, dbName) - triggers := []*cosmosdb.Trigger{ - { - ID: "renewLease", - TriggerOperation: cosmosdb.TriggerOperationAll, - TriggerType: cosmosdb.TriggerTypePre, - Body: `function trigger() { - var request = getContext().getRequest(); - var body = request.getBody(); - var date = new Date(); - body["leaseExpires"] = Math.floor(date.getTime() / 1000) + 60; - request.setBody(body); -}`, - }, - } - - triggerc := cosmosdb.NewTriggerClient(collc, collMonitors) - for _, trigger := range triggers { - _, err := triggerc.Create(ctx, trigger) - if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusConflict) { - return nil, err - } - } - return &monitors{ c: cosmosdb.NewMonitorDocumentClient(collc, collMonitors), uuid: uuid.DefaultGenerator.Generate(), diff --git a/pkg/database/openshiftclusters.go b/pkg/database/openshiftclusters.go index e3cf392e408..a41536371fe 100644 --- a/pkg/database/openshiftclusters.go +++ b/pkg/database/openshiftclusters.go @@ -59,29 +59,6 @@ type OpenShiftClusters interface { func NewOpenShiftClusters(ctx context.Context, dbc cosmosdb.DatabaseClient, dbName string) (OpenShiftClusters, error) { collc := cosmosdb.NewCollectionClient(dbc, dbName) - triggers := []*cosmosdb.Trigger{ - { - ID: "renewLease", - TriggerOperation: cosmosdb.TriggerOperationAll, - TriggerType: cosmosdb.TriggerTypePre, - Body: `function trigger() { - var request = getContext().getRequest(); - var body = request.getBody(); - var date = new Date(); - body["leaseExpires"] = Math.floor(date.getTime() / 1000) + 60; - request.setBody(body); -}`, - }, - } - - triggerc := cosmosdb.NewTriggerClient(collc, collOpenShiftClusters) - for _, trigger := range triggers { - _, err := triggerc.Create(ctx, trigger) - if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusConflict) { - return nil, err - } - } - documentClient := cosmosdb.NewOpenShiftClusterDocumentClient(collc, collOpenShiftClusters) return NewOpenShiftClustersWithProvidedClient(documentClient, collc, uuid.DefaultGenerator.Generate(), uuid.DefaultGenerator), nil } diff --git a/pkg/database/subscriptions.go b/pkg/database/subscriptions.go index 37ac9263ed1..a09dbc2e7ba 100644 --- a/pkg/database/subscriptions.go +++ b/pkg/database/subscriptions.go @@ -14,7 +14,9 @@ import ( "github.com/Azure/ARO-RP/pkg/util/uuid" ) -const SubscriptionsDequeueQuery string = `SELECT * FROM Subscriptions doc WHERE (doc.deleting ?? false) AND (doc.leaseExpires ?? 0) < GetCurrentTimestamp() / 1000` +const ( + SubscriptionsDequeueQuery string = `SELECT * FROM Subscriptions doc WHERE (doc.deleting ?? false) AND (doc.leaseExpires ?? 0) < GetCurrentTimestamp() / 1000` +) type subscriptions struct { c cosmosdb.SubscriptionDocumentClient @@ -36,41 +38,6 @@ type Subscriptions interface { func NewSubscriptions(ctx context.Context, dbc cosmosdb.DatabaseClient, dbName string) (Subscriptions, error) { collc := cosmosdb.NewCollectionClient(dbc, dbName) - triggers := []*cosmosdb.Trigger{ - { - ID: "renewLease", - TriggerOperation: cosmosdb.TriggerOperationAll, - TriggerType: cosmosdb.TriggerTypePre, - Body: `function trigger() { - var request = getContext().getRequest(); - var body = request.getBody(); - var date = new Date(); - body["leaseExpires"] = Math.floor(date.getTime() / 1000) + 60; - request.setBody(body); -}`, - }, - { - ID: "retryLater", - TriggerOperation: cosmosdb.TriggerOperationAll, - TriggerType: cosmosdb.TriggerTypePre, - Body: `function trigger() { - var request = getContext().getRequest(); - var body = request.getBody(); - var date = new Date(); - body["leaseExpires"] = Math.floor(date.getTime() / 1000) + 600; - request.setBody(body); -}`, - }, - } - - triggerc := cosmosdb.NewTriggerClient(collc, collSubscriptions) - for _, trigger := range triggers { - _, err := triggerc.Create(ctx, trigger) - if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusConflict) { - return nil, err - } - } - documentClient := cosmosdb.NewSubscriptionDocumentClient(collc, collSubscriptions) return NewSubscriptionsWithProvidedClient(documentClient, uuid.DefaultGenerator.Generate()), nil } diff --git a/pkg/dbtoken/api.go b/pkg/dbtoken/api.go deleted file mode 100644 index ad70eb1378a..00000000000 --- a/pkg/dbtoken/api.go +++ /dev/null @@ -1,8 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -type tokenResponse struct { - Token string `json:"token,omitempty"` -} diff --git a/pkg/dbtoken/client.go b/pkg/dbtoken/client.go deleted file mode 100644 index 131005d493d..00000000000 --- a/pkg/dbtoken/client.go +++ /dev/null @@ -1,90 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "crypto/tls" - "encoding/json" - "fmt" - "net/http" - "net/url" - - "github.com/Azure/go-autorest/autorest" - - "github.com/Azure/ARO-RP/pkg/env" -) - -type Client interface { - Token(context.Context, string) (string, error) -} - -type doer interface { - Do(*http.Request) (*http.Response, error) -} - -type client struct { - c doer - authorizer autorest.Authorizer - url string -} - -func NewClient(_env env.Core, authorizer autorest.Authorizer, insecureSkipVerify bool, url string) Client { - return &client{ - c: &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecureSkipVerify, - }, - // disable HTTP/2 for now: https://github.com/golang/go/issues/36026 - TLSNextProto: map[string]func(string, *tls.Conn) http.RoundTripper{}, - }, - }, - authorizer: authorizer, - url: url, - } -} - -func (c *client) Token(ctx context.Context, permission string) (string, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.url+"/token", nil) - if err != nil { - return "", err - } - - q := url.Values{ - "permission": []string{permission}, - } - req.URL.RawQuery = q.Encode() - - var tr *tokenResponse - err = c.do(req, &tr) - if err != nil { - return "", err - } - - return tr.Token, nil -} - -func (c *client) do(req *http.Request, i interface{}) (err error) { - req, err = autorest.Prepare(req, c.authorizer.WithAuthorization()) - if err != nil { - return err - } - - resp, err := c.c.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected status code %d", resp.StatusCode) - } - - if resp.Header.Get("Content-Type") != "application/json" { - return fmt.Errorf("unexpected content type %q", resp.Header.Get("Content-Type")) - } - - return json.NewDecoder(resp.Body).Decode(&i) -} diff --git a/pkg/dbtoken/client_test.go b/pkg/dbtoken/client_test.go deleted file mode 100644 index 4f942311bca..00000000000 --- a/pkg/dbtoken/client_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "io" - "net/http" - "strings" - "testing" - - "github.com/Azure/go-autorest/autorest" - - utilerror "github.com/Azure/ARO-RP/test/util/error" -) - -type fakeClient struct { - t *testing.T - wantMethod string - wantURL string - resp *http.Response - err error -} - -func (fc *fakeClient) Do(req *http.Request) (*http.Response, error) { - if req.Method != fc.wantMethod { - fc.t.Fatal(req.Method) - } - - if req.URL.String() != fc.wantURL { - fc.t.Fatal(req.URL.String()) - } - - return fc.resp, fc.err -} - -func TestClient(t *testing.T) { - ctx := context.Background() - - for _, tt := range []struct { - name string - fakeClient *fakeClient - wantToken string - wantErr string - }{ - { - name: "works", - fakeClient: &fakeClient{ - wantMethod: http.MethodPost, - wantURL: "https://localhost/token?permission=permission", - resp: &http.Response{ - StatusCode: http.StatusOK, - Header: http.Header{ - "Content-Type": []string{"application/json"}, - }, - Body: io.NopCloser(strings.NewReader(`{"token":"token"}`)), - }, - }, - wantToken: "token", - }, - { - name: "404", - fakeClient: &fakeClient{ - wantMethod: http.MethodPost, - wantURL: "https://localhost/token?permission=permission", - resp: &http.Response{ - StatusCode: http.StatusNotFound, - Body: io.NopCloser(strings.NewReader("")), - }, - }, - wantErr: "unexpected status code 404", - }, - { - name: "no content-type", - fakeClient: &fakeClient{ - wantMethod: http.MethodPost, - wantURL: "https://localhost/token?permission=permission", - resp: &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(strings.NewReader("")), - }, - }, - wantErr: `unexpected content type ""`, - }, - } { - t.Run(tt.name, func(t *testing.T) { - tt.fakeClient.t = t - - c := &client{ - c: tt.fakeClient, - authorizer: &autorest.NullAuthorizer{}, - url: "https://localhost", - } - - token, err := c.Token(ctx, "permission") - utilerror.AssertErrorMessage(t, err, tt.wantErr) - - if token != tt.wantToken { - t.Error(token) - } - }) - } -} diff --git a/pkg/dbtoken/log.go b/pkg/dbtoken/log.go deleted file mode 100644 index ffeece3e11d..00000000000 --- a/pkg/dbtoken/log.go +++ /dev/null @@ -1,78 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "io" - "net/http" - "time" - - "github.com/sirupsen/logrus" - - "github.com/Azure/ARO-RP/pkg/portal/middleware" -) - -type logResponseWriter struct { - http.ResponseWriter - - statusCode int - bytes int -} - -func (w *logResponseWriter) Write(b []byte) (int, error) { - n, err := w.ResponseWriter.Write(b) - w.bytes += n - return n, err -} - -func (w *logResponseWriter) WriteHeader(statusCode int) { - w.ResponseWriter.WriteHeader(statusCode) - w.statusCode = statusCode -} - -type logReadCloser struct { - io.ReadCloser - - bytes int -} - -func (rc *logReadCloser) Read(b []byte) (int, error) { - n, err := rc.ReadCloser.Read(b) - rc.bytes += n - return n, err -} - -func Log(log *logrus.Entry) func(http.Handler) http.Handler { - return func(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t := time.Now() - - r.Body = &logReadCloser{ReadCloser: r.Body} - w = &logResponseWriter{ResponseWriter: w, statusCode: http.StatusOK} - - username, _ := r.Context().Value(middleware.ContextKeyUsername).(string) - - log := log.WithFields(logrus.Fields{ - "request_method": r.Method, - "request_path": r.URL.Path, - "request_proto": r.Proto, - "request_remote_addr": r.RemoteAddr, - "request_user_agent": r.UserAgent(), - "username": username, - }) - log.Print("read request") - - defer func() { - log.WithFields(logrus.Fields{ - "body_read_bytes": r.Body.(*logReadCloser).bytes, - "body_written_bytes": w.(*logResponseWriter).bytes, - "duration": time.Since(t).Seconds(), - "response_status_code": w.(*logResponseWriter).statusCode, - }).Print("sent response") - }() - - h.ServeHTTP(w, r) - }) - } -} diff --git a/pkg/dbtoken/permissions.go b/pkg/dbtoken/permissions.go deleted file mode 100644 index d6b1a59c05e..00000000000 --- a/pkg/dbtoken/permissions.go +++ /dev/null @@ -1,35 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "net/http" - "os" - - "github.com/Azure/ARO-RP/pkg/database/cosmosdb" -) - -func ConfigurePermissions(ctx context.Context, dbid string, userc cosmosdb.UserClient) error { - gateway := os.Getenv("AZURE_GATEWAY_SERVICE_PRINCIPAL_ID") - - _, err := userc.Create(ctx, &cosmosdb.User{ - ID: gateway, - }) - if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusConflict) { - return err - } - - permc := cosmosdb.NewPermissionClient(userc, gateway) - _, err = permc.Create(ctx, &cosmosdb.Permission{ - ID: "gateway", - PermissionMode: cosmosdb.PermissionModeRead, - Resource: "dbs/" + dbid + "/colls/Gateway", - }) - if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusConflict) { - return err - } - - return nil -} diff --git a/pkg/dbtoken/refresher.go b/pkg/dbtoken/refresher.go deleted file mode 100644 index c4283bb1ff3..00000000000 --- a/pkg/dbtoken/refresher.go +++ /dev/null @@ -1,109 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "fmt" - "runtime/debug" - "sync/atomic" - "time" - - "github.com/Azure/go-autorest/autorest" - "github.com/sirupsen/logrus" - - "github.com/Azure/ARO-RP/pkg/database/cosmosdb" - "github.com/Azure/ARO-RP/pkg/env" - "github.com/Azure/ARO-RP/pkg/metrics" - "github.com/Azure/ARO-RP/pkg/util/heartbeat" - utilrecover "github.com/Azure/ARO-RP/pkg/util/recover" -) - -type Refresher interface { - Run(context.Context) error - HasSyncedOnce() bool -} - -type refresher struct { - log *logrus.Entry - c Client - - dbc cosmosdb.DatabaseClient - permission string - - lastRefresh atomic.Value //time.Time - - m metrics.Emitter - metricPrefix string - tokenRefreshed bool -} - -func NewRefresher(log *logrus.Entry, env env.Core, authorizer autorest.Authorizer, insecureSkipVerify bool, dbc cosmosdb.DatabaseClient, permission string, m metrics.Emitter, metricPrefix string, url string) Refresher { - return &refresher{ - log: log, - c: NewClient(env, authorizer, insecureSkipVerify, url), - - dbc: dbc, - permission: permission, - - m: m, - metricPrefix: metricPrefix, - } -} - -func (r *refresher) checkRefreshAndReset() bool { - if r.tokenRefreshed { - r.tokenRefreshed = false - return true - } - return false -} - -func (r *refresher) Run(ctx context.Context) error { - defer utilrecover.Panic(r.log) - - go heartbeat.EmitHeartbeat(r.log, r.m, r.metricPrefix+".dbtokenrefresh", nil, r.checkRefreshAndReset) - - t := time.NewTicker(10 * time.Second) - defer t.Stop() - - for { - err := r.runOnce(ctx) - if err != nil { - r.log.Error(err) - } else { - r.lastRefresh.Store(time.Now()) - r.tokenRefreshed = true - } - - <-t.C - } -} - -func (r *refresher) runOnce(ctx context.Context) (err error) { - // extra hardening to prevent a panic under runOnce taking out the refresher - // goroutine - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("panic: %s (original err: %v)\n\n%s", e, err, string(debug.Stack())) - } - }() - - timeoutCtx, done := context.WithTimeout(ctx, time.Minute) - defer done() - - token, err := r.c.Token(timeoutCtx, r.permission) - if err != nil { - return err - } - - r.dbc.SetAuthorizer(cosmosdb.NewTokenAuthorizer(token)) - - return nil -} - -func (r *refresher) HasSyncedOnce() bool { - _, ok := r.lastRefresh.Load().(time.Time) - return ok -} diff --git a/pkg/dbtoken/server.go b/pkg/dbtoken/server.go deleted file mode 100644 index 829416b676d..00000000000 --- a/pkg/dbtoken/server.go +++ /dev/null @@ -1,179 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/json" - "log" - "net" - "net/http" - "regexp" - "strings" - "time" - - "github.com/go-chi/chi/v5" - "github.com/sirupsen/logrus" - - "github.com/Azure/ARO-RP/pkg/database/cosmosdb" - "github.com/Azure/ARO-RP/pkg/env" - "github.com/Azure/ARO-RP/pkg/metrics" - "github.com/Azure/ARO-RP/pkg/portal/middleware" - "github.com/Azure/ARO-RP/pkg/util/heartbeat" - "github.com/Azure/ARO-RP/pkg/util/oidc" - "github.com/Azure/ARO-RP/pkg/util/uuid" -) - -var rxValidPermission = regexp.MustCompile("^[a-z]{1,20}$") - -type Server interface { - Run(context.Context) error -} - -type server struct { - env env.Core - log *logrus.Entry - accessLog *logrus.Entry - l net.Listener - verifier oidc.Verifier - permissionClientFactory func(userid string) cosmosdb.PermissionClient - m metrics.Emitter -} - -func NewServer( - ctx context.Context, - env env.Core, - log *logrus.Entry, - accessLog *logrus.Entry, - l net.Listener, - servingKey *rsa.PrivateKey, - servingCerts []*x509.Certificate, - verifier oidc.Verifier, - userc cosmosdb.UserClient, - m metrics.Emitter, -) (Server, error) { - config := &tls.Config{ - Certificates: []tls.Certificate{ - { - PrivateKey: servingKey, - }, - }, - NextProtos: []string{"h2", "http/1.1"}, - SessionTicketsDisabled: true, - MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{ - tls.CurveP256, - tls.X25519, - }, - } - - for _, cert := range servingCerts { - config.Certificates[0].Certificate = append(config.Certificates[0].Certificate, cert.Raw) - } - - return &server{ - env: env, - log: log, - accessLog: accessLog, - l: tls.NewListener(l, config), - verifier: verifier, - permissionClientFactory: func(userid string) cosmosdb.PermissionClient { - return cosmosdb.NewPermissionClient(userc, userid) - }, - m: m, - }, nil -} - -func healthCheck(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) -} - -func (s *server) Run(ctx context.Context) error { - go heartbeat.EmitHeartbeat(s.log, s.m, "dbtoken.heartbeat", nil, func() bool { return true }) - - logHandler := Log(s.accessLog) - panicMiddleware := middleware.Panic(s.log) - - chiRouter := chi.NewMux() - - chiRouter.Use(panicMiddleware, logHandler) - - chiRouter.Get("/healthz/ready", healthCheck) - - tokenRefresh := panicMiddleware(s.authenticate(logHandler(http.HandlerFunc(s.token)))) - chiRouter.With(s.authenticate).Post("/token", tokenRefresh.ServeHTTP) - - srv := &http.Server{ - Handler: chiRouter, - ReadTimeout: 10 * time.Second, - IdleTimeout: 2 * time.Minute, - ErrorLog: log.New(s.log.Writer(), "", 0), - BaseContext: func(net.Listener) context.Context { return ctx }, - } - - return srv.Serve(s.l) -} - -func (s *server) authenticate(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - if !strings.HasPrefix(r.Header.Get("Authorization"), "Bearer ") { - http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) - return - } - - token, err := s.verifier.Verify(ctx, strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")) - if err != nil { - http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) - return - } - - if valid := uuid.IsValid(token.Subject()); !valid { - http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) - return - } - - ctx = context.WithValue(ctx, middleware.ContextKeyUsername, token.Subject()) - r = r.WithContext(ctx) - - h.ServeHTTP(w, r) - }) -} - -func (s *server) token(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - permission := r.URL.Query().Get("permission") - if !rxValidPermission.MatchString(permission) { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - return - } - - username, _ := ctx.Value(middleware.ContextKeyUsername).(string) - permc := s.permissionClientFactory(username) - - perm, err := permc.Get(ctx, permission) - if err != nil { - s.log.Error(err) - if cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) { - http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) - } else { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - } - return - } - - w.Header().Set("Content-Type", "application/json") - - e := json.NewEncoder(w) - e.SetIndent("", " ") - - _ = e.Encode(&tokenResponse{ - Token: perm.Token, - }) -} diff --git a/pkg/dbtoken/server_test.go b/pkg/dbtoken/server_test.go deleted file mode 100644 index 7af4c4d9450..00000000000 --- a/pkg/dbtoken/server_test.go +++ /dev/null @@ -1,300 +0,0 @@ -package dbtoken - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "encoding/json" - "errors" - "net/http" - "net/url" - "testing" - - "github.com/golang/mock/gomock" - "github.com/sirupsen/logrus" - - "github.com/Azure/ARO-RP/pkg/database/cosmosdb" - mock_cosmosdb "github.com/Azure/ARO-RP/pkg/util/mocks/cosmosdb" - "github.com/Azure/ARO-RP/pkg/util/oidc" - "github.com/Azure/ARO-RP/test/util/listener" -) - -func TestServer(t *testing.T) { - ctx := context.Background() - - for _, tt := range []struct { - name string - permissionClientFactory func(controller *gomock.Controller) func(userid string) cosmosdb.PermissionClient - req *http.Request - wantStatusCode int - wantToken string - }{ - { - name: "GET /random returns 404", - req: &http.Request{ - Method: http.MethodGet, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/random", - }, - }, - wantStatusCode: http.StatusNotFound, - }, - { - name: "GET /healthz/ready returns 200", - req: &http.Request{ - Method: http.MethodGet, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/healthz/ready", - }, - }, - wantStatusCode: http.StatusOK, - }, - { - name: "GET unauthorized /token returns 405", - req: &http.Request{ - Method: http.MethodGet, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - }, - }, - wantStatusCode: http.StatusMethodNotAllowed, - }, - { - name: "POST /token?permission=good returns 403 (no auth)", - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=good", - }, - }, - wantStatusCode: http.StatusForbidden, - }, - { - name: "POST /token?permission=good returns 403 (empty subject)", - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=good", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": ""}`}, - }, - }, - wantStatusCode: http.StatusForbidden, - }, - { - name: "POST /token?permission=good returns 403 (subject not UUID)", - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=good", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "xyz"}`}, - }, - }, - wantStatusCode: http.StatusForbidden, - }, - { - name: "POST /token returns 400", - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "00000000-0000-0000-0000-000000000000"}`}, - }, - }, - wantStatusCode: http.StatusBadRequest, - }, - { - name: "POST /token?permission=bad! returns 400", - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=bad!", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "00000000-0000-0000-0000-000000000000"}`}, - }, - }, - wantStatusCode: http.StatusBadRequest, - }, - { - name: "POST /token?permission=notexist returns 400", - permissionClientFactory: func(controller *gomock.Controller) func(userid string) cosmosdb.PermissionClient { - return func(userid string) cosmosdb.PermissionClient { - permc := mock_cosmosdb.NewMockPermissionClient(controller) - permc.EXPECT().Get(gomock.Any(), "notexist").Return(nil, &cosmosdb.Error{StatusCode: http.StatusNotFound}) - return permc - } - }, - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=notexist", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "00000000-0000-0000-0000-000000000000"}`}, - }, - }, - wantStatusCode: http.StatusBadRequest, - }, - { - name: "POST /token?permission=perm and database error returns 500", - permissionClientFactory: func(controller *gomock.Controller) func(userid string) cosmosdb.PermissionClient { - return func(userid string) cosmosdb.PermissionClient { - permc := mock_cosmosdb.NewMockPermissionClient(controller) - permc.EXPECT().Get(gomock.Any(), "perm").Return(nil, errors.New("sad database")) - return permc - } - }, - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=perm", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "00000000-0000-0000-0000-000000000000"}`}, - }, - }, - wantStatusCode: http.StatusInternalServerError, - }, - { - name: "get /token?permission=perm returns 405", - permissionClientFactory: func(controller *gomock.Controller) func(userid string) cosmosdb.PermissionClient { - return func(userid string) cosmosdb.PermissionClient { - permc := mock_cosmosdb.NewMockPermissionClient(controller) - permc.EXPECT().Get(gomock.Any(), "perm").Return(&cosmosdb.Permission{ - Token: "token", - }, nil) - return permc - } - }, - req: &http.Request{ - Method: http.MethodGet, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=perm", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "00000000-0000-0000-0000-000000000000"}`}, - }, - }, - wantStatusCode: http.StatusMethodNotAllowed, - }, - { - name: "POST /token?permission=perm returns 200", - permissionClientFactory: func(controller *gomock.Controller) func(userid string) cosmosdb.PermissionClient { - return func(userid string) cosmosdb.PermissionClient { - permc := mock_cosmosdb.NewMockPermissionClient(controller) - permc.EXPECT().Get(gomock.Any(), "perm").Return(&cosmosdb.Permission{ - Token: "token", - }, nil) - return permc - } - }, - req: &http.Request{ - Method: http.MethodPost, - URL: &url.URL{ - Scheme: "http", - Host: "localhost", - Path: "/token", - RawQuery: "permission=perm", - }, - Header: http.Header{ - "Authorization": []string{`Bearer {"sub": "00000000-0000-0000-0000-000000000000"}`}, - }, - }, - wantStatusCode: http.StatusOK, - wantToken: "token", - }, - } { - t.Run(tt.name, func(t *testing.T) { - controller := gomock.NewController(t) - defer controller.Finish() - - l := listener.NewListener() - defer l.Close() - - s := &server{ - log: logrus.NewEntry(logrus.StandardLogger()), - accessLog: logrus.NewEntry(logrus.StandardLogger()), - l: l, - verifier: &oidc.NoopVerifier{}, - } - - if tt.permissionClientFactory != nil { - s.permissionClientFactory = tt.permissionClientFactory(controller) - } - - go func() { - _ = s.Run(ctx) - }() - - c := &http.Client{ - Transport: &http.Transport{ - DialContext: l.DialContext, - }, - } - - resp, err := c.Do(tt.req) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - - if resp.StatusCode != tt.wantStatusCode { - t.Error(resp.StatusCode) - } - - if tt.wantToken == "" { - return - } - - if resp.Header.Get("Content-Type") != "application/json" { - t.Fatal(resp.Header.Get("Content-Type")) - } - - var tr *tokenResponse - err = json.NewDecoder(resp.Body).Decode(&tr) - if err != nil { - t.Fatal(err) - } - - if tr.Token != tt.wantToken { - t.Error(tr.Token) - } - }) - } -} diff --git a/pkg/deploy/assets/databases-development.json b/pkg/deploy/assets/databases-development.json index 04ae1697451..191670b7f6f 100644 --- a/pkg/deploy/assets/databases-development.json +++ b/pkg/deploy/assets/databases-development.json @@ -260,6 +260,114 @@ } }, "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('databaseName'))]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('databaseName'), 'Subscriptions')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', parameters('databaseName'), '/Subscriptions/renewLease')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "renewLease", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('databaseName'))]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('databaseName'), 'Subscriptions')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', parameters('databaseName'), '/Subscriptions/retryLater')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 600;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "retryLater", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('databaseName'))]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('databaseName'), 'Billing')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', parameters('databaseName'), '/Billing/setCreationBillingTimeStamp')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tvar now = Math.floor(date.getTime() / 1000);\n\t\t\t\tvar billingBody = body[\"billing\"];\n\t\t\t\tif (!billingBody[\"creationTime\"]) {\n\t\t\t\t\tbillingBody[\"creationTime\"] = now;\n\t\t\t\t}\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "setCreationBillingTimeStamp", + "triggerOperation": "Create", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('databaseName'))]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('databaseName'), 'Billing')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', parameters('databaseName'), '/Billing/setDeletionBillingTimeStamp')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tvar now = Math.floor(date.getTime() / 1000);\n\t\t\t\tvar billingBody = body[\"billing\"];\n\t\t\t\tif (!billingBody[\"creationTime\"]) {\n\t\t\t\t\tbillingBody[\"creationTime\"] = now;\n\t\t\t\t}\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "setDeletionBillingTimeStamp", + "triggerOperation": "Replace", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('databaseName'))]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('databaseName'), 'OpenShiftClusters')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', parameters('databaseName'), '/OpenShiftClusters/renewLease')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "renewLease", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('databaseName'))]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('databaseName'), 'Monitors')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', parameters('databaseName'), '/Monitors/renewLease')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "renewLease", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" } ] } diff --git a/pkg/deploy/assets/gateway-production-parameters.json b/pkg/deploy/assets/gateway-production-parameters.json index 5fc523531a7..09a897cb5bf 100644 --- a/pkg/deploy/assets/gateway-production-parameters.json +++ b/pkg/deploy/assets/gateway-production-parameters.json @@ -17,12 +17,6 @@ "databaseAccountName": { "value": "" }, - "dbtokenClientId": { - "value": "" - }, - "dbtokenUrl": { - "value": "" - }, "fluentbitImage": { "value": "" }, diff --git a/pkg/deploy/assets/gateway-production.json b/pkg/deploy/assets/gateway-production.json index cb24f1874f2..16d477b1e1e 100644 --- a/pkg/deploy/assets/gateway-production.json +++ b/pkg/deploy/assets/gateway-production.json @@ -17,12 +17,6 @@ "databaseAccountName": { "type": "string" }, - "dbtokenClientId": { - "type": "string" - }, - "dbtokenUrl": { - "type": "string" - }, "fluentbitImage": { "type": "string" }, @@ -308,7 +302,7 @@ "autoUpgradeMinorVersion": true, "settings": {}, "protectedSettings": { - "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'ACRRESOURCEID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('acrResourceId')),''')\n','AZURECLOUDNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureCloudName')),''')\n','AZURESECPACKQUALYSURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackQualysUrl')),''')\n','AZURESECPACKVSATENANTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackVSATenantId')),''')\n','DATABASEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('databaseAccountName')),''')\n','DBTOKENCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('dbtokenClientId')),''')\n','DBTOKENURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('dbtokenUrl')),''')\n','MDMFRONTENDURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdmFrontendUrl')),''')\n','MDSDENVIRONMENT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdsdEnvironment')),''')\n','FLUENTBITIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fluentbitImage')),''')\n','GATEWAYMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayMdsdConfigVersion')),''')\n','GATEWAYDOMAINS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayDomains')),''')\n','GATEWAYFEATURES=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayFeatures')),''')\n','KEYVAULTDNSSUFFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultDNSSuffix')),''')\n','KEYVAULTPREFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultPrefix')),''')\n','RPIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpImage')),''')\n','RPMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdmAccount')),''')\n','RPMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdAccount')),''')\n','RPMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdNamespace')),''')\n','MDMIMAGE=''/distroless/genevamdm:2.2024.517.533-b73893-20240522t0954@sha256:939df9d7b6660874697f8ebed1fe56504f86d92f99801a9dc6fd98e9176d3f75''\n','LOCATION=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().location),''')\n','SUBSCRIPTIONID=$(base64 -d \u003c\u003c\u003c''',base64(subscription().subscriptionId),''')\n','RESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().name),''')\n','\n',base64ToString('IyEvYmluL2Jhc2gKCmVjaG8gInNldHRpbmcgc3NoIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uIgojIFdlIG5lZWQgdG8gbWFudWFsbHkgc2V0IFBhc3N3b3JkQXV0aGVudGljYXRpb24gdG8gdHJ1ZSBpbiBvcmRlciBmb3IgdGhlIFZNU1MgQWNjZXNzIEpJVCB0byB3b3JrCnNlZCAtaSAncy9QYXNzd29yZEF1dGhlbnRpY2F0aW9uIG5vL1Bhc3N3b3JkQXV0aGVudGljYXRpb24geWVzL2cnIC9ldGMvc3NoL3NzaGRfY29uZmlnCnN5c3RlbWN0bCByZWxvYWQgc3NoZC5zZXJ2aWNlCgojQWRkaW5nIHJldHJ5IGxvZ2ljIHRvIHl1bSBjb21tYW5kcyBpbiBvcmRlciB0byBhdm9pZCBzdGFsbGluZyBvdXQgb24gcmVzb3VyY2UgbG9ja3MKZWNobyAicnVubmluZyBSSFVJIGZpeCIKZm9yIGF0dGVtcHQgaW4gezEuLjYwfTsgZG8KICB5dW0gdXBkYXRlIC15IC0tZGlzYWJsZXJlcG89JyonIC0tZW5hYmxlcmVwbz0ncmh1aS1taWNyb3NvZnQtYXp1cmUqJyAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAicnVubmluZyB5dW0gdXBkYXRlIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlIC0tYWxsb3dlcmFzaW5nICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgplY2hvICJleHRlbmRpbmcgcGFydGl0aW9uIHRhYmxlIgojIExpbnV4IGJsb2NrIGRldmljZXMgYXJlIGluY29uc2lzdGVudGx5IG5hbWVkCiMgaXQncyBkaWZmaWN1bHQgdG8gdGllIHRoZSBsdm0gcHYgdG8gdGhlIHBoeXNpY2FsIGRpc2sgdXNpbmcgL2Rldi9kaXNrIGZpbGVzLCB3aGljaCBpcyB3aHkgbHZzIGlzIHVzZWQgaGVyZQpwaHlzaWNhbF9kaXNrPSIkKGx2cyAtbyBkZXZpY2VzIC1hIHwgaGVhZCAtbjIgfCB0YWlsIC1uMSB8IGN1dCAtZCAnICcgLWYgMyB8IGN1dCAtZCBcKCAtZiAxIHwgdHIgLWQgJ1s6ZGlnaXQ6XScpIgpncm93cGFydCAiJHBoeXNpY2FsX2Rpc2siIDIKCmVjaG8gImV4dGVuZGluZyBmaWxlc3lzdGVtcyIKbHZleHRlbmQgLWwgKzIwJUZSRUUgL2Rldi9yb290dmcvcm9vdGx2Cnhmc19ncm93ZnMgLwoKbHZleHRlbmQgLWwgKzEwMCVGUkVFIC9kZXYvcm9vdHZnL3Zhcmx2Cnhmc19ncm93ZnMgL3ZhcgoKcnBtIC0taW1wb3J0IGh0dHBzOi8vZGwuZmVkb3JhcHJvamVjdC5vcmcvcHViL2VwZWwvUlBNLUdQRy1LRVktRVBFTC04CnJwbSAtLWltcG9ydCBodHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20va2V5cy9taWNyb3NvZnQuYXNjCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSBpbnN0YWxsIGh0dHBzOi8vZGwuZmVkb3JhcHJvamVjdC5vcmcvcHViL2VwZWwvZXBlbC1yZWxlYXNlLWxhdGVzdC04Lm5vYXJjaC5ycG0gJiYgYnJlYWsKICBpZiBbWyAke2F0dGVtcHR9IC1sdCA2MCBdXTsgdGhlbiBzbGVlcCAzMDsgZWxzZSBleGl0IDE7IGZpCmRvbmUKCmVjaG8gImNvbmZpZ3VyaW5nIGxvZ3JvdGF0ZSIKCiMgZ2F0ZXdheV9sb2dkaXIgaXMgYSByZWFkb25seSB2YXJpYWJsZSB0aGF0IHNwZWNpZmllcyB0aGUgaG9zdCBwYXRoIG1vdW50IHBvaW50IGZvciB0aGUgZ2F0ZXdheSBjb250YWluZXIgbG9nIGZpbGUKIyBmb3IgdGhlIHB1cnBvc2Ugb2Ygcm90YXRpbmcgdGhlIGdhdGV3YXkgbG9ncwpkZWNsYXJlIC1yIGdhdGV3YXlfbG9nZGlyPScvdmFyL2xvZy9hcm8tZ2F0ZXdheScKCmNhdCA+L2V0Yy9sb2dyb3RhdGUuY29uZiA8PEVPRgojIHNlZSAibWFuIGxvZ3JvdGF0ZSIgZm9yIGRldGFpbHMKIyByb3RhdGUgbG9nIGZpbGVzIHdlZWtseQp3ZWVrbHkKCiMga2VlcCAyIHdlZWtzIHdvcnRoIG9mIGJhY2tsb2dzCnJvdGF0ZSAyCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBsb2cgZmlsZXMgYWZ0ZXIgcm90YXRpbmcgb2xkIG9uZXMKY3JlYXRlCgojIHVzZSBkYXRlIGFzIGEgc3VmZml4IG9mIHRoZSByb3RhdGVkIGZpbGUKZGF0ZWV4dAoKIyB1bmNvbW1lbnQgdGhpcyBpZiB5b3Ugd2FudCB5b3VyIGxvZyBmaWxlcyBjb21wcmVzc2VkCmNvbXByZXNzCgojIFJQTSBwYWNrYWdlcyBkcm9wIGxvZyByb3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIHRoaXMgZGlyZWN0b3J5CmluY2x1ZGUgL2V0Yy9sb2dyb3RhdGUuZAoKIyBubyBwYWNrYWdlcyBvd24gd3RtcCBhbmQgYnRtcCAtLSB3ZSdsbCByb3RhdGUgdGhlbSBoZXJlCi92YXIvbG9nL3d0bXAgewogICAgbW9udGhseQogICAgY3JlYXRlIDA2NjQgcm9vdCB1dG1wCiAgICAgICAgbWluc2l6ZSAxTQogICAgcm90YXRlIDEKfQoKL3Zhci9sb2cvYnRtcCB7CiAgICBtaXNzaW5nb2sKICAgIG1vbnRobHkKICAgIGNyZWF0ZSAwNjAwIHJvb3QgdXRtcAogICAgcm90YXRlIDEKfQoKIyBNYXhpbXVtIGxvZyBkaXJlY3Rvcnkgc2l6ZSBpcyAxMDBHIHdpdGggdGhpcyBjb25maWd1cmF0aW9uCiMgU2V0dGluZyBsaW1pdCB0byAxMDBHIHRvIGFsbG93IHNwYWNlIGZvciBvdGhlciBsb2dnaW5nIHNlcnZpY2VzCiMgY29weXRydW5jYXRlIGlzIGEgY3JpdGljYWwgb3B0aW9uIHVzZWQgdG8gcHJldmVudCBsb2dzIGZyb20gYmVpbmcgc2hpcHBlZCB0d2ljZQoke2dhdGV3YXlfbG9nZGlyfSB7CiAgICBzaXplIDIwRwogICAgcm90YXRlIDUKICAgIGNyZWF0ZSAwNjAwIHJvb3Qgcm9vdAogICAgY29weXRydW5jYXRlCiAgICBub29sZGRpcgogICAgY29tcHJlc3MKfQpFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIHl1bSByZXBvc2l0b3J5IGFuZCBydW5uaW5nIHl1bSB1cGRhdGUiCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwoKW2F6dXJlY29yZV0KbmFtZT1henVyZWNvcmUKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmVjb3JlCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPW5vCkVPRgoKc2VtYW5hZ2UgZmNvbnRleHQgLWEgLXQgdmFyX2xvZ190ICIvdmFyL2xvZy9qb3VybmFsKC8uKik/Igpta2RpciAtcCAvdmFyL2xvZy9qb3VybmFsCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSBpbnN0YWxsIGNsYW1hdiBhenNlYy1jbGFtYXYgYXpzZWMtbW9uaXRvciBhenVyZS1jbGkgYXp1cmUtbWRzZCBhenVyZS1zZWN1cml0eSBwb2RtYW4tZG9ja2VyIG9wZW5zc2wtcGVybCBweXRob24zICYmIGJyZWFrCiAgIyBoYWNrIC0gd2UgYXJlIGluc3RhbGxpbmcgcHl0aG9uMyBvbiBob3N0cyBkdWUgdG8gYW4gaXNzdWUgd2l0aCBBenVyZSBMaW51eCBFeHRlbnNpb25zIGh0dHBzOi8vZ2l0aHViLmNvbS9BenVyZS9henVyZS1saW51eC1leHRlbnNpb25zL3B1bGwvMTUwNQogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAiYXBwbHlpbmcgZmlyZXdhbGwgcnVsZXMiCiMgaHR0cHM6Ly9hY2Nlc3MucmVkaGF0LmNvbS9zZWN1cml0eS9jdmUvY3ZlLTIwMjAtMTM0MDEKY2F0ID4vZXRjL3N5c2N0bC5kLzAyLWRpc2FibGUtYWNjZXB0LXJhLmNvbmYgPDwnRU9GJwpuZXQuaXB2Ni5jb25mLmFsbC5hY2NlcHRfcmE9MApFT0YKCmNhdCA+L2V0Yy9zeXNjdGwuZC8wMS1kaXNhYmxlLWNvcmUuY29uZiA8PCdFT0YnCmtlcm5lbC5jb3JlX3BhdHRlcm4gPSB8L2Jpbi90cnVlCkVPRgpzeXNjdGwgLS1zeXN0ZW0KCmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTgwL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD04MDgxL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD00NDMvdGNwIC0tcGVybWFuZW50CgplY2hvICJsb2dnaW5nIGludG8gcHJvZCBhY3IiCmV4cG9ydCBBWlVSRV9DTE9VRF9OQU1FPSRBWlVSRUNMT1VETkFNRQpheiBsb2dpbiAtaSAtLWFsbG93LW5vLXN1YnNjcmlwdGlvbnMKCiMgVGhlIG1hbmFnZWQgaWRlbnRpdHkgdGhhdCB0aGUgVk0gcnVucyBhcyBvbmx5IGhhcyBhIHNpbmdsZSByb2xlYXNzaWdubWVudC4KIyBUaGlzIHJvbGUgYXNzaWdubWVudCBpcyBBQ1JQdWxsIHdoaWNoIGlzIG5vdCBuZWNlc3NhcmlseSBwcmVzZW50IGluIHRoZQojIHN1YnNjcmlwdGlvbiB3ZSdyZSBkZXBsb3lpbmcgaW50by4gIElmIHRoZSBpZGVudGl0eSBkb2VzIG5vdCBoYXZlIGFueQojIHJvbGUgYXNzaWdubWVudHMgc2NvcGVkIG9uIHRoZSBzdWJzY3JpcHRpb24gd2UncmUgZGVwbG95aW5nIGludG8sIGl0IHdpbGwKIyBub3Qgc2hvdyBvbiBheiBsb2dpbiAtaSwgd2hpY2ggaXMgd2h5IHRoZSBiZWxvdyBsaW5lIGlzIGNvbW1lbnRlZC4KIyBheiBhY2NvdW50IHNldCAtcyAiJFNVQlNDUklQVElPTklEIgoKIyBTdXBwcmVzcyBlbXVsYXRpb24gb3V0cHV0IGZvciBwb2RtYW4gaW5zdGVhZCBvZiBkb2NrZXIgZm9yIGF6IGFjciBjb21wYXRhYmlsaXR5Cm1rZGlyIC1wIC9ldGMvY29udGFpbmVycy8KdG91Y2ggL2V0Yy9jb250YWluZXJzL25vZG9ja2VyCgpta2RpciAtcCAvcm9vdC8uZG9ja2VyClJFR0lTVFJZX0FVVEhfRklMRT0vcm9vdC8uZG9ja2VyL2NvbmZpZy5qc29uIGF6IGFjciBsb2dpbiAtLW5hbWUgIiQoc2VkIC1lICdzfC4qL3x8JyA8PDwiJEFDUlJFU09VUkNFSUQiKSIKCk1ETUlNQUdFPSIke1JQSU1BR0UlJS8qfS8ke01ETUlNQUdFIyovfSIKZG9ja2VyIHB1bGwgIiRNRE1JTUFHRSIKZG9ja2VyIHB1bGwgIiRSUElNQUdFIgpkb2NrZXIgcHVsbCAiJEZMVUVOVEJJVElNQUdFIgoKYXogbG9nb3V0CgplY2hvICJjb25maWd1cmluZyBmbHVlbnRiaXQgc2VydmljZSIKbWtkaXIgLXAgL2V0Yy9mbHVlbnRiaXQvCm1rZGlyIC1wIC92YXIvbGliL2ZsdWVudAoKY2F0ID4vZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZiA8PCdFT0YnCltJTlBVVF0KCU5hbWUgc3lzdGVtZAoJVGFnIGpvdXJuYWxkCglTeXN0ZW1kX0ZpbHRlciBfQ09NTT1hcm8KCURCIC92YXIvbGliL2ZsdWVudC9qb3VybmFsZGIKCltGSUxURVJdCglOYW1lIG1vZGlmeQoJTWF0Y2ggam91cm5hbGQKCVJlbW92ZV93aWxkY2FyZCBfCglSZW1vdmUgVElNRVNUQU1QCgpbT1VUUFVUXQoJTmFtZSBmb3J3YXJkCglNYXRjaCAqCglQb3J0IDI5MjMwCkVPRgoKZWNobyAiRkxVRU5UQklUSU1BR0U9JEZMVUVOVEJJVElNQUdFIiA+L2V0Yy9zeXNjb25maWcvZmx1ZW50Yml0CgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZmx1ZW50Yml0LnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldApTdGFydExpbWl0SW50ZXJ2YWxTZWM9MAoKW1NlcnZpY2VdClJlc3RhcnRTZWM9MXMKRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2ZsdWVudGJpdApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1zZWN1cml0eS1vcHQgbGFiZWw9ZGlzYWJsZSBcCiAgLS1lbnRyeXBvaW50IC9vcHQvdGQtYWdlbnQtYml0L2Jpbi90ZC1hZ2VudC1iaXQgXAogIC0tbmV0PWhvc3QgXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtdiAvZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZjovZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZiBcCiAgLXYgL3Zhci9saWIvZmx1ZW50Oi92YXIvbGliL2ZsdWVudDp6IFwKICAtdiAvdmFyL2xvZy9qb3VybmFsOi92YXIvbG9nL2pvdXJuYWw6cm8gXAogIC12IC9ldGMvbWFjaGluZS1pZDovZXRjL21hY2hpbmUtaWQ6cm8gXAogICRGTFVFTlRCSVRJTUFHRSBcCiAgLWMgL2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmYKCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wICVOClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9NQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKZWNobyAiY29uZmlndXJpbmcgbWRtIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvbWRtIDw8RU9GCk1ETUZST05URU5EVVJMPSckTURNRlJPTlRFTkRVUkwnCk1ETUlNQUdFPSckTURNSU1BR0UnCk1ETVNPVVJDRUVOVklST05NRU5UPSckTE9DQVRJT04nCk1ETVNPVVJDRVJPTEU9Z2F0ZXdheQpNRE1TT1VSQ0VST0xFSU5TVEFOQ0U9JyQoaG9zdG5hbWUpJwpFT0YKCm1rZGlyIC92YXIvZXR3CmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZG0uc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL21kbQpFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1lbnRyeXBvaW50IC91c3Ivc2Jpbi9NZXRyaWNzRXh0ZW5zaW9uIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLW0gMmcgXAogIC12IC9ldGMvbWRtLnBlbTovZXRjL21kbS5wZW0gXAogIC12IC92YXIvZXR3Oi92YXIvZXR3OnogXAogICRNRE1JTUFHRSBcCiAgLUNlcnRGaWxlIC9ldGMvbWRtLnBlbSBcCiAgLUZyb250RW5kVXJsICRNRE1GUk9OVEVORFVSTCBcCiAgLUxvZ2dlciBDb25zb2xlIFwKICAtTG9nTGV2ZWwgV2FybmluZyBcCiAgLVByaXZhdGVLZXlGaWxlIC9ldGMvbWRtLnBlbSBcCiAgLVNvdXJjZUVudmlyb25tZW50ICRNRE1TT1VSQ0VFTlZJUk9OTUVOVCBcCiAgLVNvdXJjZVJvbGUgJE1ETVNPVVJDRVJPTEUgXAogIC1Tb3VyY2VSb2xlSW5zdGFuY2UgJE1ETVNPVVJDRVJPTEVJTlNUQU5DRQpFeGVjU3RvcD0vdXNyL2Jpbi9kb2NrZXIgc3RvcCAlTgpSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIGFyby1nYXRld2F5IHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvYXJvLWdhdGV3YXkgPDxFT0YKQUNSX1JFU09VUkNFX0lEPSckQUNSUkVTT1VSQ0VJRCcKREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKQVpVUkVfREJUT0tFTl9DTElFTlRfSUQ9JyREQlRPS0VOQ0xJRU5USUQnCkRCVE9LRU5fVVJMPSckREJUT0tFTlVSTCcKTURNX0FDQ09VTlQ9IiRSUE1ETUFDQ09VTlQiCk1ETV9OQU1FU1BBQ0U9R2F0ZXdheQpHQVRFV0FZX0RPTUFJTlM9JyRHQVRFV0FZRE9NQUlOUycKR0FURVdBWV9GRUFUVVJFUz0nJEdBVEVXQVlGRUFUVVJFUycKUlBJTUFHRT0nJFJQSU1BR0UnCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Fyby1nYXRld2F5LnNlcnZpY2UgPDxFT0YKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvYXJvLWdhdGV3YXkKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0UHJlPS91c3IvYmluL21rZGlyIC1wICR7Z2F0ZXdheV9sb2dkaXJ9CkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQUNSX1JFU09VUkNFX0lEIFwKICAtZSBEQVRBQkFTRV9BQ0NPVU5UX05BTUUgXAogIC1lIEFaVVJFX0RCVE9LRU5fQ0xJRU5UX0lEIFwKICAtZSBEQlRPS0VOX1VSTCBcCiAgLWUgR0FURVdBWV9ET01BSU5TIFwKICAtZSBHQVRFV0FZX0ZFQVRVUkVTIFwKICAtZSBNRE1fQUNDT1VOVCBcCiAgLWUgTURNX05BTUVTUEFDRSBcCiAgLW0gMmcgXAogIC1wIDgwOjgwODAgXAogIC1wIDgwODE6ODA4MSBcCiAgLXAgNDQzOjg0NDMgXAogIC12IC9ydW4vc3lzdGVtZC9qb3VybmFsOi9ydW4vc3lzdGVtZC9qb3VybmFsIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAtdiAke2dhdGV3YXlfbG9nZGlyfTovY3RyLmxvZzp6IFwKICBcJFJQSU1BR0UgXAogIGdhdGV3YXkKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgLXQgMzYwMCAlTgpUaW1lb3V0U3RvcFNlYz0zNjAwClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKY2hjb24gLVIgc3lzdGVtX3U6b2JqZWN0X3I6dmFyX2xvZ190OnMwIC92YXIvb3B0L21pY3Jvc29mdC9saW51eG1vbmFnZW50Cgpta2RpciAtcCAvdmFyL2xpYi93YWFnZW50L01pY3Jvc29mdC5BenVyZS5LZXlWYXVsdC5TdG9yZQoKZWNobyAiY29uZmlndXJpbmcgbWRzZCBhbmQgbWRtIHNlcnZpY2VzIgpmb3IgdmFyIGluICJtZHNkIiAibWRtIjsgZG8KY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Rvd25sb2FkLSR2YXItY3JlZGVudGlhbHMuc2VydmljZSA8PEVPRgpbVW5pdF0KRGVzY3JpcHRpb249UGVyaW9kaWMgJHZhciBjcmVkZW50aWFscyByZWZyZXNoCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4ZWNTdGFydD0vdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCAkdmFyCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Rvd25sb2FkLSR2YXItY3JlZGVudGlhbHMudGltZXIgPDxFT0YKW1VuaXRdCkRlc2NyaXB0aW9uPVBlcmlvZGljICR2YXIgY3JlZGVudGlhbHMgcmVmcmVzaApBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbVGltZXJdCk9uQm9vdFNlYz0wbWluCk9uQ2FsZW5kYXI9MC8xMjowMDowMApBY2N1cmFjeVNlYz01cwoKW0luc3RhbGxdCldhbnRlZEJ5PXRpbWVycy50YXJnZXQKRU9GCmRvbmUKCmNhdCA+L3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggPDxFT0YKIyEvYmluL2Jhc2gKc2V0IC1ldQoKQ09NUE9ORU5UPSJcJDEiCmVjaG8gIkRvd25sb2FkIFwkQ09NUE9ORU5UIGNyZWRlbnRpYWxzIgoKVEVNUF9ESVI9XCQobWt0ZW1wIC1kKQpleHBvcnQgQVpVUkVfQ09ORklHX0RJUj1cJChta3RlbXAgLWQpCgplY2hvICJMb2dnaW5nIGludG8gQXp1cmUuLi4iClJFVFJJRVM9Mwp3aGlsZSBbICJcJFJFVFJJRVMiIC1ndCAwIF07IGRvCiAgICBpZiBheiBsb2dpbiAtaSAtLWFsbG93LW5vLXN1YnNjcmlwdGlvbnMKICAgIHRoZW4KICAgICAgICBlY2hvICJheiBsb2dpbiBzdWNjZXNzZnVsIgogICAgICAgIGJyZWFrCiAgICBlbHNlCiAgICAgICAgZWNobyAiYXogbG9naW4gZmFpbGVkLiBSZXRyeWluZy4uLiIKICAgICAgICBsZXQgUkVUUklFUy09MQogICAgICAgIHNsZWVwIDUKICAgIGZpCmRvbmUKCnRyYXAgImNsZWFudXAiIEVYSVQKCmNsZWFudXAoKSB7CiAgYXogbG9nb3V0CiAgW1sgIlwkVEVNUF9ESVIiID1+IC90bXAvLisgXV0gJiYgcm0gLXJmIFwkVEVNUF9ESVIKICBbWyAiXCRBWlVSRV9DT05GSUdfRElSIiA9fiAvdG1wLy4rIF1dICYmIHJtIC1yZiBcJEFaVVJFX0NPTkZJR19ESVIKfQoKaWYgWyAiXCRDT01QT05FTlQiID0gIm1kbSIgXTsgdGhlbgogIENVUlJFTlRfQ0VSVF9GSUxFPSIvZXRjL21kbS5wZW0iCmVsaWYgWyAiXCRDT01QT05FTlQiID0gIm1kc2QiIF07IHRoZW4KICBDVVJSRU5UX0NFUlRfRklMRT0iL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUvbWRzZC5wZW0iCmVsc2UKICBlY2hvIEludmFsaWQgdXNhZ2UgJiYgZXhpdCAxCmZpCgpTRUNSRVRfTkFNRT0iZ3d5LVwke0NPTVBPTkVOVH0iCk5FV19DRVJUX0ZJTEU9IlwkVEVNUF9ESVIvXCRDT01QT05FTlQucGVtIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNX07IGRvCiAgYXoga2V5dmF1bHQgc2VjcmV0IGRvd25sb2FkIC0tZmlsZSBcJE5FV19DRVJUX0ZJTEUgLS1pZCAiaHR0cHM6Ly8kS0VZVkFVTFRQUkVGSVgtZ3d5LiRLRVlWQVVMVEROU1NVRkZJWC9zZWNyZXRzL1wkU0VDUkVUX05BTUUiICYmIGJyZWFrCiAgaWYgW1sgXCRhdHRlbXB0IC1sdCA1IF1dOyB0aGVuIHNsZWVwIDEwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKaWYgWyAtZiBcJE5FV19DRVJUX0ZJTEUgXTsgdGhlbgogIGlmIFsgIlwkQ09NUE9ORU5UIiA9ICJtZHNkIiBdOyB0aGVuCiAgICBjaG93biBzeXNsb2c6c3lzbG9nIFwkTkVXX0NFUlRfRklMRQogIGVsc2UKICAgIHNlZCAtaSAtbmUgJzEsL0VORCBDRVJUSUZJQ0FURS8gcCcgXCRORVdfQ0VSVF9GSUxFCiAgZmkKCiAgbmV3X2NlcnRfc249IlwkKG9wZW5zc2wgeDUwOSAtaW4gIlwkTkVXX0NFUlRfRklMRSIgLW5vb3V0IC1zZXJpYWwgfCBhd2sgLUY9ICd7cHJpbnQgXCQyfScpIgogIGN1cnJlbnRfY2VydF9zbj0iXCQob3BlbnNzbCB4NTA5IC1pbiAiXCRDVVJSRU5UX0NFUlRfRklMRSIgLW5vb3V0IC1zZXJpYWwgfCBhd2sgLUY9ICd7cHJpbnQgXCQyfScpIgogIGlmIFtbICEgLXogXCRuZXdfY2VydF9zbiBdXSAmJiBbWyBcJG5ld19jZXJ0X3NuICE9ICJcJGN1cnJlbnRfY2VydF9zbiIgXV07IHRoZW4KICAgIGVjaG8gdXBkYXRpbmcgY2VydGlmaWNhdGUgZm9yIFwkQ09NUE9ORU5UCiAgICBjaG1vZCAwNjAwIFwkTkVXX0NFUlRfRklMRQogICAgbXYgXCRORVdfQ0VSVF9GSUxFIFwkQ1VSUkVOVF9DRVJUX0ZJTEUKICBmaQplbHNlCiAgZWNobyBGYWlsZWQgdG8gcmVmcmVzaCBjZXJ0aWZpY2F0ZSBmb3IgXCRDT01QT05FTlQgJiYgZXhpdCAxCmZpCkVPRgoKY2htb2QgdSt4IC91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoCgpzeXN0ZW1jdGwgZW5hYmxlIGRvd25sb2FkLW1kc2QtY3JlZGVudGlhbHMudGltZXIKc3lzdGVtY3RsIGVuYWJsZSBkb3dubG9hZC1tZG0tY3JlZGVudGlhbHMudGltZXIKCi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIG1kc2QKL3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggbWRtCk1EU0RDRVJUSUZJQ0FURVNBTj0kKG9wZW5zc2wgeDUwOSAtaW4gL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUvbWRzZC5wZW0gLW5vb3V0IC1zdWJqZWN0IHwgc2VkIC1lICdzLy4qQ04gPSAvLycpCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnNlcnZpY2UgPDxFT0YKW1VuaXRdCkRlc2NyaXB0aW9uPVdhdGNoIGZvciBjaGFuZ2VzIGluIG1kbS5wZW0gYW5kIHJlc3RhcnRzIHRoZSBtZG0gc2VydmljZQoKW1NlcnZpY2VdClR5cGU9b25lc2hvdApFeGVjU3RhcnQ9L3Vzci9iaW4vc3lzdGVtY3RsIHJlc3RhcnQgbWRtLnNlcnZpY2UKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS93YXRjaC1tZG0tY3JlZGVudGlhbHMucGF0aCA8PEVPRgpbUGF0aF0KUGF0aE1vZGlmaWVkPS9ldGMvbWRtLnBlbQoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKc3lzdGVtY3RsIGVuYWJsZSB3YXRjaC1tZG0tY3JlZGVudGlhbHMucGF0aApzeXN0ZW1jdGwgc3RhcnQgd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGgKCm1rZGlyIC9ldGMvc3lzdGVtZC9zeXN0ZW0vbWRzZC5zZXJ2aWNlLmQKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL21kc2Quc2VydmljZS5kL292ZXJyaWRlLmNvbmYgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CkVPRgoKY2F0ID4vZXRjL2RlZmF1bHQvbWRzZCA8PEVPRgpNRFNEX1JPTEVfUFJFRklYPS92YXIvcnVuL21kc2QvZGVmYXVsdApNRFNEX09QVElPTlM9Ii1BIC1kIC1yIFwkTURTRF9ST0xFX1BSRUZJWCIKCmV4cG9ydCBNT05JVE9SSU5HX0dDU19FTlZJUk9OTUVOVD0nJE1EU0RFTlZJUk9OTUVOVCcKZXhwb3J0IE1PTklUT1JJTkdfR0NTX0FDQ09VTlQ9JyRSUE1EU0RBQ0NPVU5UJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfUkVHSU9OPSckTE9DQVRJT04nCmV4cG9ydCBNT05JVE9SSU5HX0dDU19BVVRIX0lEX1RZUEU9QXV0aEtleVZhdWx0CmV4cG9ydCBNT05JVE9SSU5HX0dDU19BVVRIX0lEPSckTURTRENFUlRJRklDQVRFU0FOJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfTkFNRVNQQUNFPSckUlBNRFNETkFNRVNQQUNFJwpleHBvcnQgTU9OSVRPUklOR19DT05GSUdfVkVSU0lPTj0nJEdBVEVXQVlNRFNEQ09ORklHVkVSU0lPTicKZXhwb3J0IE1PTklUT1JJTkdfVVNFX0dFTkVWQV9DT05GSUdfU0VSVklDRT10cnVlCgpleHBvcnQgTU9OSVRPUklOR19URU5BTlQ9JyRMT0NBVElPTicKZXhwb3J0IE1PTklUT1JJTkdfUk9MRT1nYXRld2F5CmV4cG9ydCBNT05JVE9SSU5HX1JPTEVfSU5TVEFOQ0U9JyQoaG9zdG5hbWUpJwoKZXhwb3J0IE1EU0RfTVNHUEFDS19TT1JUX0NPTFVNTlM9MQpFT0YKCiMgc2V0dGluZyBNT05JVE9SSU5HX0dDU19BVVRIX0lEX1RZUEU9QXV0aEtleVZhdWx0IHNlZW1zIHRvIGhhdmUgY2F1c2VkIG1kc2Qgbm90CiMgdG8gaG9ub3VyIFNTTF9DRVJUX0ZJTEUgYW55IG1vcmUsIGhlYXZlbiBvbmx5IGtub3dzIHdoeS4KbWtkaXIgLXAgL3Vzci9saWIvc3NsL2NlcnRzCmNzcGxpdCAtZiAvdXNyL2xpYi9zc2wvY2VydHMvY2VydC0gLWIgJTAzZC5wZW0gL2V0Yy9wa2kvdGxzL2NlcnRzL2NhLWJ1bmRsZS5jcnQgL14kLzEgeyp9ID4vZGV2L251bGwKY19yZWhhc2ggL3Vzci9saWIvc3NsL2NlcnRzCgojIHdlIGxlYXZlIGNsaWVudElkIGJsYW5rIGFzIGxvbmcgYXMgb25seSAxIG1hbmFnZWQgaWRlbnRpdHkgYXNzaWduZWQgdG8gdm1zcwojIGlmIHdlIGhhdmUgbW9yZSB0aGFuIDEsIHdlIHdpbGwgbmVlZCB0byBwb3B1bGF0ZSB3aXRoIGNsaWVudElkIHVzZWQgZm9yIG9mZi1ub2RlIHNjYW5uaW5nCmNhdCA+L2V0Yy9kZWZhdWx0L3ZzYS1ub2Rlc2Nhbi1hZ2VudC5jb25maWcgPDxFT0YKewogICAgIk5pY2UiOiAxOSwKICAgICJUaW1lb3V0IjogMTA4MDAsCiAgICAiQ2xpZW50SWQiOiAiIiwKICAgICJUZW5hbnRJZCI6ICIkQVpVUkVTRUNQQUNLVlNBVEVOQU5USUQiLAogICAgIlF1YWx5c1N0b3JlQmFzZVVybCI6ICIkQVpVUkVTRUNQQUNLUVVBTFlTVVJMIiwKICAgICJQcm9jZXNzVGltZW91dCI6IDMwMCwKICAgICJDb21tYW5kRGVsYXkiOiAwCiAgfQpFT0YKCmVjaG8gImVuYWJsaW5nIGFybyBzZXJ2aWNlcyIKZm9yIHNlcnZpY2UgaW4gYXJvLWdhdGV3YXkgYXVvbXMgYXpzZWNkIGF6c2VjbW9uZCBtZHNkIG1kbSBjaHJvbnlkIGZsdWVudGJpdDsgZG8KICBzeXN0ZW1jdGwgZW5hYmxlICRzZXJ2aWNlLnNlcnZpY2UKZG9uZQoKZm9yIHNjYW4gaW4gYmFzZWxpbmUgY2xhbWF2IHNvZnR3YXJlOyBkbwogIC91c3IvbG9jYWwvYmluL2F6c2VjZCBjb25maWcgLXMgJHNjYW4gLWQgUDFECmRvbmUKCmVjaG8gInJlYm9vdGluZyIKcmVzdG9yZWNvbiAtUkYgL3Zhci9sb2cvKgooc2xlZXAgMzA7IHJlYm9vdCkgJgo=')))]" + "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'ACRRESOURCEID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('acrResourceId')),''')\n','AZURECLOUDNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureCloudName')),''')\n','AZURESECPACKQUALYSURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackQualysUrl')),''')\n','AZURESECPACKVSATENANTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackVSATenantId')),''')\n','DATABASEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('databaseAccountName')),''')\n','MDMFRONTENDURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdmFrontendUrl')),''')\n','MDSDENVIRONMENT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdsdEnvironment')),''')\n','FLUENTBITIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fluentbitImage')),''')\n','GATEWAYMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayMdsdConfigVersion')),''')\n','GATEWAYDOMAINS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayDomains')),''')\n','GATEWAYFEATURES=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayFeatures')),''')\n','KEYVAULTDNSSUFFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultDNSSuffix')),''')\n','KEYVAULTPREFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultPrefix')),''')\n','RPIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpImage')),''')\n','RPMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdmAccount')),''')\n','RPMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdAccount')),''')\n','RPMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdNamespace')),''')\n','MDMIMAGE=''/distroless/genevamdm:2.2024.517.533-b73893-20240522t0954@sha256:939df9d7b6660874697f8ebed1fe56504f86d92f99801a9dc6fd98e9176d3f75''\n','LOCATION=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().location),''')\n','SUBSCRIPTIONID=$(base64 -d \u003c\u003c\u003c''',base64(subscription().subscriptionId),''')\n','RESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().name),''')\n','\n',base64ToString('IyEvYmluL2Jhc2gKCmVjaG8gInNldHRpbmcgc3NoIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uIgojIFdlIG5lZWQgdG8gbWFudWFsbHkgc2V0IFBhc3N3b3JkQXV0aGVudGljYXRpb24gdG8gdHJ1ZSBpbiBvcmRlciBmb3IgdGhlIFZNU1MgQWNjZXNzIEpJVCB0byB3b3JrCnNlZCAtaSAncy9QYXNzd29yZEF1dGhlbnRpY2F0aW9uIG5vL1Bhc3N3b3JkQXV0aGVudGljYXRpb24geWVzL2cnIC9ldGMvc3NoL3NzaGRfY29uZmlnCnN5c3RlbWN0bCByZWxvYWQgc3NoZC5zZXJ2aWNlCgojQWRkaW5nIHJldHJ5IGxvZ2ljIHRvIHl1bSBjb21tYW5kcyBpbiBvcmRlciB0byBhdm9pZCBzdGFsbGluZyBvdXQgb24gcmVzb3VyY2UgbG9ja3MKZWNobyAicnVubmluZyBSSFVJIGZpeCIKZm9yIGF0dGVtcHQgaW4gezEuLjYwfTsgZG8KICB5dW0gdXBkYXRlIC15IC0tZGlzYWJsZXJlcG89JyonIC0tZW5hYmxlcmVwbz0ncmh1aS1taWNyb3NvZnQtYXp1cmUqJyAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAicnVubmluZyB5dW0gdXBkYXRlIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlIC0tYWxsb3dlcmFzaW5nICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgplY2hvICJleHRlbmRpbmcgcGFydGl0aW9uIHRhYmxlIgojIExpbnV4IGJsb2NrIGRldmljZXMgYXJlIGluY29uc2lzdGVudGx5IG5hbWVkCiMgaXQncyBkaWZmaWN1bHQgdG8gdGllIHRoZSBsdm0gcHYgdG8gdGhlIHBoeXNpY2FsIGRpc2sgdXNpbmcgL2Rldi9kaXNrIGZpbGVzLCB3aGljaCBpcyB3aHkgbHZzIGlzIHVzZWQgaGVyZQpwaHlzaWNhbF9kaXNrPSIkKGx2cyAtbyBkZXZpY2VzIC1hIHwgaGVhZCAtbjIgfCB0YWlsIC1uMSB8IGN1dCAtZCAnICcgLWYgMyB8IGN1dCAtZCBcKCAtZiAxIHwgdHIgLWQgJ1s6ZGlnaXQ6XScpIgpncm93cGFydCAiJHBoeXNpY2FsX2Rpc2siIDIKCmVjaG8gImV4dGVuZGluZyBmaWxlc3lzdGVtcyIKbHZleHRlbmQgLWwgKzIwJUZSRUUgL2Rldi9yb290dmcvcm9vdGx2Cnhmc19ncm93ZnMgLwoKbHZleHRlbmQgLWwgKzEwMCVGUkVFIC9kZXYvcm9vdHZnL3Zhcmx2Cnhmc19ncm93ZnMgL3ZhcgoKcnBtIC0taW1wb3J0IGh0dHBzOi8vZGwuZmVkb3JhcHJvamVjdC5vcmcvcHViL2VwZWwvUlBNLUdQRy1LRVktRVBFTC04CnJwbSAtLWltcG9ydCBodHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20va2V5cy9taWNyb3NvZnQuYXNjCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSBpbnN0YWxsIGh0dHBzOi8vZGwuZmVkb3JhcHJvamVjdC5vcmcvcHViL2VwZWwvZXBlbC1yZWxlYXNlLWxhdGVzdC04Lm5vYXJjaC5ycG0gJiYgYnJlYWsKICBpZiBbWyAke2F0dGVtcHR9IC1sdCA2MCBdXTsgdGhlbiBzbGVlcCAzMDsgZWxzZSBleGl0IDE7IGZpCmRvbmUKCmVjaG8gImNvbmZpZ3VyaW5nIGxvZ3JvdGF0ZSIKCiMgZ2F0ZXdheV9sb2dkaXIgaXMgYSByZWFkb25seSB2YXJpYWJsZSB0aGF0IHNwZWNpZmllcyB0aGUgaG9zdCBwYXRoIG1vdW50IHBvaW50IGZvciB0aGUgZ2F0ZXdheSBjb250YWluZXIgbG9nIGZpbGUKIyBmb3IgdGhlIHB1cnBvc2Ugb2Ygcm90YXRpbmcgdGhlIGdhdGV3YXkgbG9ncwpkZWNsYXJlIC1yIGdhdGV3YXlfbG9nZGlyPScvdmFyL2xvZy9hcm8tZ2F0ZXdheScKCmNhdCA+L2V0Yy9sb2dyb3RhdGUuY29uZiA8PEVPRgojIHNlZSAibWFuIGxvZ3JvdGF0ZSIgZm9yIGRldGFpbHMKIyByb3RhdGUgbG9nIGZpbGVzIHdlZWtseQp3ZWVrbHkKCiMga2VlcCAyIHdlZWtzIHdvcnRoIG9mIGJhY2tsb2dzCnJvdGF0ZSAyCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBsb2cgZmlsZXMgYWZ0ZXIgcm90YXRpbmcgb2xkIG9uZXMKY3JlYXRlCgojIHVzZSBkYXRlIGFzIGEgc3VmZml4IG9mIHRoZSByb3RhdGVkIGZpbGUKZGF0ZWV4dAoKIyB1bmNvbW1lbnQgdGhpcyBpZiB5b3Ugd2FudCB5b3VyIGxvZyBmaWxlcyBjb21wcmVzc2VkCmNvbXByZXNzCgojIFJQTSBwYWNrYWdlcyBkcm9wIGxvZyByb3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIHRoaXMgZGlyZWN0b3J5CmluY2x1ZGUgL2V0Yy9sb2dyb3RhdGUuZAoKIyBubyBwYWNrYWdlcyBvd24gd3RtcCBhbmQgYnRtcCAtLSB3ZSdsbCByb3RhdGUgdGhlbSBoZXJlCi92YXIvbG9nL3d0bXAgewogICAgbW9udGhseQogICAgY3JlYXRlIDA2NjQgcm9vdCB1dG1wCiAgICAgICAgbWluc2l6ZSAxTQogICAgcm90YXRlIDEKfQoKL3Zhci9sb2cvYnRtcCB7CiAgICBtaXNzaW5nb2sKICAgIG1vbnRobHkKICAgIGNyZWF0ZSAwNjAwIHJvb3QgdXRtcAogICAgcm90YXRlIDEKfQoKIyBNYXhpbXVtIGxvZyBkaXJlY3Rvcnkgc2l6ZSBpcyAxMDBHIHdpdGggdGhpcyBjb25maWd1cmF0aW9uCiMgU2V0dGluZyBsaW1pdCB0byAxMDBHIHRvIGFsbG93IHNwYWNlIGZvciBvdGhlciBsb2dnaW5nIHNlcnZpY2VzCiMgY29weXRydW5jYXRlIGlzIGEgY3JpdGljYWwgb3B0aW9uIHVzZWQgdG8gcHJldmVudCBsb2dzIGZyb20gYmVpbmcgc2hpcHBlZCB0d2ljZQoke2dhdGV3YXlfbG9nZGlyfSB7CiAgICBzaXplIDIwRwogICAgcm90YXRlIDUKICAgIGNyZWF0ZSAwNjAwIHJvb3Qgcm9vdAogICAgY29weXRydW5jYXRlCiAgICBub29sZGRpcgogICAgY29tcHJlc3MKfQpFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIHl1bSByZXBvc2l0b3J5IGFuZCBydW5uaW5nIHl1bSB1cGRhdGUiCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwoKW2F6dXJlY29yZV0KbmFtZT1henVyZWNvcmUKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmVjb3JlCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPW5vCkVPRgoKc2VtYW5hZ2UgZmNvbnRleHQgLWEgLXQgdmFyX2xvZ190ICIvdmFyL2xvZy9qb3VybmFsKC8uKik/Igpta2RpciAtcCAvdmFyL2xvZy9qb3VybmFsCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSBpbnN0YWxsIGNsYW1hdiBhenNlYy1jbGFtYXYgYXpzZWMtbW9uaXRvciBhenVyZS1jbGkgYXp1cmUtbWRzZCBhenVyZS1zZWN1cml0eSBwb2RtYW4tZG9ja2VyIG9wZW5zc2wtcGVybCBweXRob24zICYmIGJyZWFrCiAgIyBoYWNrIC0gd2UgYXJlIGluc3RhbGxpbmcgcHl0aG9uMyBvbiBob3N0cyBkdWUgdG8gYW4gaXNzdWUgd2l0aCBBenVyZSBMaW51eCBFeHRlbnNpb25zIGh0dHBzOi8vZ2l0aHViLmNvbS9BenVyZS9henVyZS1saW51eC1leHRlbnNpb25zL3B1bGwvMTUwNQogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAiYXBwbHlpbmcgZmlyZXdhbGwgcnVsZXMiCiMgaHR0cHM6Ly9hY2Nlc3MucmVkaGF0LmNvbS9zZWN1cml0eS9jdmUvY3ZlLTIwMjAtMTM0MDEKY2F0ID4vZXRjL3N5c2N0bC5kLzAyLWRpc2FibGUtYWNjZXB0LXJhLmNvbmYgPDwnRU9GJwpuZXQuaXB2Ni5jb25mLmFsbC5hY2NlcHRfcmE9MApFT0YKCmNhdCA+L2V0Yy9zeXNjdGwuZC8wMS1kaXNhYmxlLWNvcmUuY29uZiA8PCdFT0YnCmtlcm5lbC5jb3JlX3BhdHRlcm4gPSB8L2Jpbi90cnVlCkVPRgpzeXNjdGwgLS1zeXN0ZW0KCmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTgwL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD04MDgxL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD00NDMvdGNwIC0tcGVybWFuZW50CgplY2hvICJsb2dnaW5nIGludG8gcHJvZCBhY3IiCmV4cG9ydCBBWlVSRV9DTE9VRF9OQU1FPSRBWlVSRUNMT1VETkFNRQpheiBsb2dpbiAtaSAtLWFsbG93LW5vLXN1YnNjcmlwdGlvbnMKCiMgVGhlIG1hbmFnZWQgaWRlbnRpdHkgdGhhdCB0aGUgVk0gcnVucyBhcyBvbmx5IGhhcyBhIHNpbmdsZSByb2xlYXNzaWdubWVudC4KIyBUaGlzIHJvbGUgYXNzaWdubWVudCBpcyBBQ1JQdWxsIHdoaWNoIGlzIG5vdCBuZWNlc3NhcmlseSBwcmVzZW50IGluIHRoZQojIHN1YnNjcmlwdGlvbiB3ZSdyZSBkZXBsb3lpbmcgaW50by4gIElmIHRoZSBpZGVudGl0eSBkb2VzIG5vdCBoYXZlIGFueQojIHJvbGUgYXNzaWdubWVudHMgc2NvcGVkIG9uIHRoZSBzdWJzY3JpcHRpb24gd2UncmUgZGVwbG95aW5nIGludG8sIGl0IHdpbGwKIyBub3Qgc2hvdyBvbiBheiBsb2dpbiAtaSwgd2hpY2ggaXMgd2h5IHRoZSBiZWxvdyBsaW5lIGlzIGNvbW1lbnRlZC4KIyBheiBhY2NvdW50IHNldCAtcyAiJFNVQlNDUklQVElPTklEIgoKIyBTdXBwcmVzcyBlbXVsYXRpb24gb3V0cHV0IGZvciBwb2RtYW4gaW5zdGVhZCBvZiBkb2NrZXIgZm9yIGF6IGFjciBjb21wYXRhYmlsaXR5Cm1rZGlyIC1wIC9ldGMvY29udGFpbmVycy8KdG91Y2ggL2V0Yy9jb250YWluZXJzL25vZG9ja2VyCgpta2RpciAtcCAvcm9vdC8uZG9ja2VyClJFR0lTVFJZX0FVVEhfRklMRT0vcm9vdC8uZG9ja2VyL2NvbmZpZy5qc29uIGF6IGFjciBsb2dpbiAtLW5hbWUgIiQoc2VkIC1lICdzfC4qL3x8JyA8PDwiJEFDUlJFU09VUkNFSUQiKSIKCk1ETUlNQUdFPSIke1JQSU1BR0UlJS8qfS8ke01ETUlNQUdFIyovfSIKZG9ja2VyIHB1bGwgIiRNRE1JTUFHRSIKZG9ja2VyIHB1bGwgIiRSUElNQUdFIgpkb2NrZXIgcHVsbCAiJEZMVUVOVEJJVElNQUdFIgoKYXogbG9nb3V0CgplY2hvICJjb25maWd1cmluZyBmbHVlbnRiaXQgc2VydmljZSIKbWtkaXIgLXAgL2V0Yy9mbHVlbnRiaXQvCm1rZGlyIC1wIC92YXIvbGliL2ZsdWVudAoKY2F0ID4vZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZiA8PCdFT0YnCltJTlBVVF0KCU5hbWUgc3lzdGVtZAoJVGFnIGpvdXJuYWxkCglTeXN0ZW1kX0ZpbHRlciBfQ09NTT1hcm8KCURCIC92YXIvbGliL2ZsdWVudC9qb3VybmFsZGIKCltGSUxURVJdCglOYW1lIG1vZGlmeQoJTWF0Y2ggam91cm5hbGQKCVJlbW92ZV93aWxkY2FyZCBfCglSZW1vdmUgVElNRVNUQU1QCgpbT1VUUFVUXQoJTmFtZSBmb3J3YXJkCglNYXRjaCAqCglQb3J0IDI5MjMwCkVPRgoKZWNobyAiRkxVRU5UQklUSU1BR0U9JEZMVUVOVEJJVElNQUdFIiA+L2V0Yy9zeXNjb25maWcvZmx1ZW50Yml0CgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZmx1ZW50Yml0LnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldApTdGFydExpbWl0SW50ZXJ2YWxTZWM9MAoKW1NlcnZpY2VdClJlc3RhcnRTZWM9MXMKRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2ZsdWVudGJpdApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1zZWN1cml0eS1vcHQgbGFiZWw9ZGlzYWJsZSBcCiAgLS1lbnRyeXBvaW50IC9vcHQvdGQtYWdlbnQtYml0L2Jpbi90ZC1hZ2VudC1iaXQgXAogIC0tbmV0PWhvc3QgXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtdiAvZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZjovZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZiBcCiAgLXYgL3Zhci9saWIvZmx1ZW50Oi92YXIvbGliL2ZsdWVudDp6IFwKICAtdiAvdmFyL2xvZy9qb3VybmFsOi92YXIvbG9nL2pvdXJuYWw6cm8gXAogIC12IC9ldGMvbWFjaGluZS1pZDovZXRjL21hY2hpbmUtaWQ6cm8gXAogICRGTFVFTlRCSVRJTUFHRSBcCiAgLWMgL2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmYKCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wICVOClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9NQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKZWNobyAiY29uZmlndXJpbmcgbWRtIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvbWRtIDw8RU9GCk1ETUZST05URU5EVVJMPSckTURNRlJPTlRFTkRVUkwnCk1ETUlNQUdFPSckTURNSU1BR0UnCk1ETVNPVVJDRUVOVklST05NRU5UPSckTE9DQVRJT04nCk1ETVNPVVJDRVJPTEU9Z2F0ZXdheQpNRE1TT1VSQ0VST0xFSU5TVEFOQ0U9JyQoaG9zdG5hbWUpJwpFT0YKCm1rZGlyIC92YXIvZXR3CmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZG0uc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL21kbQpFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1lbnRyeXBvaW50IC91c3Ivc2Jpbi9NZXRyaWNzRXh0ZW5zaW9uIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLW0gMmcgXAogIC12IC9ldGMvbWRtLnBlbTovZXRjL21kbS5wZW0gXAogIC12IC92YXIvZXR3Oi92YXIvZXR3OnogXAogICRNRE1JTUFHRSBcCiAgLUNlcnRGaWxlIC9ldGMvbWRtLnBlbSBcCiAgLUZyb250RW5kVXJsICRNRE1GUk9OVEVORFVSTCBcCiAgLUxvZ2dlciBDb25zb2xlIFwKICAtTG9nTGV2ZWwgV2FybmluZyBcCiAgLVByaXZhdGVLZXlGaWxlIC9ldGMvbWRtLnBlbSBcCiAgLVNvdXJjZUVudmlyb25tZW50ICRNRE1TT1VSQ0VFTlZJUk9OTUVOVCBcCiAgLVNvdXJjZVJvbGUgJE1ETVNPVVJDRVJPTEUgXAogIC1Tb3VyY2VSb2xlSW5zdGFuY2UgJE1ETVNPVVJDRVJPTEVJTlNUQU5DRQpFeGVjU3RvcD0vdXNyL2Jpbi9kb2NrZXIgc3RvcCAlTgpSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIGFyby1nYXRld2F5IHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvYXJvLWdhdGV3YXkgPDxFT0YKQUNSX1JFU09VUkNFX0lEPSckQUNSUkVTT1VSQ0VJRCcKREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKTURNX0FDQ09VTlQ9IiRSUE1ETUFDQ09VTlQiCk1ETV9OQU1FU1BBQ0U9R2F0ZXdheQpHQVRFV0FZX0RPTUFJTlM9JyRHQVRFV0FZRE9NQUlOUycKR0FURVdBWV9GRUFUVVJFUz0nJEdBVEVXQVlGRUFUVVJFUycKUlBJTUFHRT0nJFJQSU1BR0UnCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Fyby1nYXRld2F5LnNlcnZpY2UgPDxFT0YKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvYXJvLWdhdGV3YXkKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0UHJlPS91c3IvYmluL21rZGlyIC1wICR7Z2F0ZXdheV9sb2dkaXJ9CkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQUNSX1JFU09VUkNFX0lEIFwKICAtZSBEQVRBQkFTRV9BQ0NPVU5UX05BTUUgXAogIC1lIEdBVEVXQVlfRE9NQUlOUyBcCiAgLWUgR0FURVdBWV9GRUFUVVJFUyBcCiAgLWUgTURNX0FDQ09VTlQgXAogIC1lIE1ETV9OQU1FU1BBQ0UgXAogIC1tIDJnIFwKICAtcCA4MDo4MDgwIFwKICAtcCA4MDgxOjgwODEgXAogIC1wIDQ0Mzo4NDQzIFwKICAtdiAvcnVuL3N5c3RlbWQvam91cm5hbDovcnVuL3N5c3RlbWQvam91cm5hbCBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgLXYgJHtnYXRld2F5X2xvZ2Rpcn06L2N0ci5sb2c6eiBcCiAgXCRSUElNQUdFIFwKICBnYXRld2F5CkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wIC10IDM2MDAgJU4KVGltZW91dFN0b3BTZWM9MzYwMApSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmNoY29uIC1SIHN5c3RlbV91Om9iamVjdF9yOnZhcl9sb2dfdDpzMCAvdmFyL29wdC9taWNyb3NvZnQvbGludXhtb25hZ2VudAoKbWtkaXIgLXAgL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUKCmVjaG8gImNvbmZpZ3VyaW5nIG1kc2QgYW5kIG1kbSBzZXJ2aWNlcyIKZm9yIHZhciBpbiAibWRzZCIgIm1kbSI7IGRvCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9kb3dubG9hZC0kdmFyLWNyZWRlbnRpYWxzLnNlcnZpY2UgPDxFT0YKW1VuaXRdCkRlc2NyaXB0aW9uPVBlcmlvZGljICR2YXIgY3JlZGVudGlhbHMgcmVmcmVzaAoKW1NlcnZpY2VdClR5cGU9b25lc2hvdApFeGVjU3RhcnQ9L3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggJHZhcgpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9kb3dubG9hZC0kdmFyLWNyZWRlbnRpYWxzLnRpbWVyIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1QZXJpb2RpYyAkdmFyIGNyZWRlbnRpYWxzIHJlZnJlc2gKQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldAoKW1RpbWVyXQpPbkJvb3RTZWM9MG1pbgpPbkNhbGVuZGFyPTAvMTI6MDA6MDAKQWNjdXJhY3lTZWM9NXMKCltJbnN0YWxsXQpXYW50ZWRCeT10aW1lcnMudGFyZ2V0CkVPRgpkb25lCgpjYXQgPi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIDw8RU9GCiMhL2Jpbi9iYXNoCnNldCAtZXUKCkNPTVBPTkVOVD0iXCQxIgplY2hvICJEb3dubG9hZCBcJENPTVBPTkVOVCBjcmVkZW50aWFscyIKClRFTVBfRElSPVwkKG1rdGVtcCAtZCkKZXhwb3J0IEFaVVJFX0NPTkZJR19ESVI9XCQobWt0ZW1wIC1kKQoKZWNobyAiTG9nZ2luZyBpbnRvIEF6dXJlLi4uIgpSRVRSSUVTPTMKd2hpbGUgWyAiXCRSRVRSSUVTIiAtZ3QgMCBdOyBkbwogICAgaWYgYXogbG9naW4gLWkgLS1hbGxvdy1uby1zdWJzY3JpcHRpb25zCiAgICB0aGVuCiAgICAgICAgZWNobyAiYXogbG9naW4gc3VjY2Vzc2Z1bCIKICAgICAgICBicmVhawogICAgZWxzZQogICAgICAgIGVjaG8gImF6IGxvZ2luIGZhaWxlZC4gUmV0cnlpbmcuLi4iCiAgICAgICAgbGV0IFJFVFJJRVMtPTEKICAgICAgICBzbGVlcCA1CiAgICBmaQpkb25lCgp0cmFwICJjbGVhbnVwIiBFWElUCgpjbGVhbnVwKCkgewogIGF6IGxvZ291dAogIFtbICJcJFRFTVBfRElSIiA9fiAvdG1wLy4rIF1dICYmIHJtIC1yZiBcJFRFTVBfRElSCiAgW1sgIlwkQVpVUkVfQ09ORklHX0RJUiIgPX4gL3RtcC8uKyBdXSAmJiBybSAtcmYgXCRBWlVSRV9DT05GSUdfRElSCn0KCmlmIFsgIlwkQ09NUE9ORU5UIiA9ICJtZG0iIF07IHRoZW4KICBDVVJSRU5UX0NFUlRfRklMRT0iL2V0Yy9tZG0ucGVtIgplbGlmIFsgIlwkQ09NUE9ORU5UIiA9ICJtZHNkIiBdOyB0aGVuCiAgQ1VSUkVOVF9DRVJUX0ZJTEU9Ii92YXIvbGliL3dhYWdlbnQvTWljcm9zb2Z0LkF6dXJlLktleVZhdWx0LlN0b3JlL21kc2QucGVtIgplbHNlCiAgZWNobyBJbnZhbGlkIHVzYWdlICYmIGV4aXQgMQpmaQoKU0VDUkVUX05BTUU9Imd3eS1cJHtDT01QT05FTlR9IgpORVdfQ0VSVF9GSUxFPSJcJFRFTVBfRElSL1wkQ09NUE9ORU5ULnBlbSIKZm9yIGF0dGVtcHQgaW4gezEuLjV9OyBkbwogIGF6IGtleXZhdWx0IHNlY3JldCBkb3dubG9hZCAtLWZpbGUgXCRORVdfQ0VSVF9GSUxFIC0taWQgImh0dHBzOi8vJEtFWVZBVUxUUFJFRklYLWd3eS4kS0VZVkFVTFRETlNTVUZGSVgvc2VjcmV0cy9cJFNFQ1JFVF9OQU1FIiAmJiBicmVhawogIGlmIFtbIFwkYXR0ZW1wdCAtbHQgNSBdXTsgdGhlbiBzbGVlcCAxMDsgZWxzZSBleGl0IDE7IGZpCmRvbmUKCmlmIFsgLWYgXCRORVdfQ0VSVF9GSUxFIF07IHRoZW4KICBpZiBbICJcJENPTVBPTkVOVCIgPSAibWRzZCIgXTsgdGhlbgogICAgY2hvd24gc3lzbG9nOnN5c2xvZyBcJE5FV19DRVJUX0ZJTEUKICBlbHNlCiAgICBzZWQgLWkgLW5lICcxLC9FTkQgQ0VSVElGSUNBVEUvIHAnIFwkTkVXX0NFUlRfRklMRQogIGZpCgogIG5ld19jZXJ0X3NuPSJcJChvcGVuc3NsIHg1MDkgLWluICJcJE5FV19DRVJUX0ZJTEUiIC1ub291dCAtc2VyaWFsIHwgYXdrIC1GPSAne3ByaW50IFwkMn0nKSIKICBjdXJyZW50X2NlcnRfc249IlwkKG9wZW5zc2wgeDUwOSAtaW4gIlwkQ1VSUkVOVF9DRVJUX0ZJTEUiIC1ub291dCAtc2VyaWFsIHwgYXdrIC1GPSAne3ByaW50IFwkMn0nKSIKICBpZiBbWyAhIC16IFwkbmV3X2NlcnRfc24gXV0gJiYgW1sgXCRuZXdfY2VydF9zbiAhPSAiXCRjdXJyZW50X2NlcnRfc24iIF1dOyB0aGVuCiAgICBlY2hvIHVwZGF0aW5nIGNlcnRpZmljYXRlIGZvciBcJENPTVBPTkVOVAogICAgY2htb2QgMDYwMCBcJE5FV19DRVJUX0ZJTEUKICAgIG12IFwkTkVXX0NFUlRfRklMRSBcJENVUlJFTlRfQ0VSVF9GSUxFCiAgZmkKZWxzZQogIGVjaG8gRmFpbGVkIHRvIHJlZnJlc2ggY2VydGlmaWNhdGUgZm9yIFwkQ09NUE9ORU5UICYmIGV4aXQgMQpmaQpFT0YKCmNobW9kIHUreCAvdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaAoKc3lzdGVtY3RsIGVuYWJsZSBkb3dubG9hZC1tZHNkLWNyZWRlbnRpYWxzLnRpbWVyCnN5c3RlbWN0bCBlbmFibGUgZG93bmxvYWQtbWRtLWNyZWRlbnRpYWxzLnRpbWVyCgovdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCBtZHNkCi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIG1kbQpNRFNEQ0VSVElGSUNBVEVTQU49JChvcGVuc3NsIHg1MDkgLWluIC92YXIvbGliL3dhYWdlbnQvTWljcm9zb2Z0LkF6dXJlLktleVZhdWx0LlN0b3JlL21kc2QucGVtIC1ub291dCAtc3ViamVjdCB8IHNlZCAtZSAncy8uKkNOID0gLy8nKQoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL3dhdGNoLW1kbS1jcmVkZW50aWFscy5zZXJ2aWNlIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1XYXRjaCBmb3IgY2hhbmdlcyBpbiBtZG0ucGVtIGFuZCByZXN0YXJ0cyB0aGUgbWRtIHNlcnZpY2UKCltTZXJ2aWNlXQpUeXBlPW9uZXNob3QKRXhlY1N0YXJ0PS91c3IvYmluL3N5c3RlbWN0bCByZXN0YXJ0IG1kbS5zZXJ2aWNlCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGggPDxFT0YKW1BhdGhdClBhdGhNb2RpZmllZD0vZXRjL21kbS5wZW0KCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCnN5c3RlbWN0bCBlbmFibGUgd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGgKc3lzdGVtY3RsIHN0YXJ0IHdhdGNoLW1kbS1jcmVkZW50aWFscy5wYXRoCgpta2RpciAvZXRjL3N5c3RlbWQvc3lzdGVtL21kc2Quc2VydmljZS5kCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZHNkLnNlcnZpY2UuZC9vdmVycmlkZS5jb25mIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApFT0YKCmNhdCA+L2V0Yy9kZWZhdWx0L21kc2QgPDxFT0YKTURTRF9ST0xFX1BSRUZJWD0vdmFyL3J1bi9tZHNkL2RlZmF1bHQKTURTRF9PUFRJT05TPSItQSAtZCAtciBcJE1EU0RfUk9MRV9QUkVGSVgiCgpleHBvcnQgTU9OSVRPUklOR19HQ1NfRU5WSVJPTk1FTlQ9JyRNRFNERU5WSVJPTk1FTlQnCmV4cG9ydCBNT05JVE9SSU5HX0dDU19BQ0NPVU5UPSckUlBNRFNEQUNDT1VOVCcKZXhwb3J0IE1PTklUT1JJTkdfR0NTX1JFR0lPTj0nJExPQ0FUSU9OJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfQVVUSF9JRF9UWVBFPUF1dGhLZXlWYXVsdApleHBvcnQgTU9OSVRPUklOR19HQ1NfQVVUSF9JRD0nJE1EU0RDRVJUSUZJQ0FURVNBTicKZXhwb3J0IE1PTklUT1JJTkdfR0NTX05BTUVTUEFDRT0nJFJQTURTRE5BTUVTUEFDRScKZXhwb3J0IE1PTklUT1JJTkdfQ09ORklHX1ZFUlNJT049JyRHQVRFV0FZTURTRENPTkZJR1ZFUlNJT04nCmV4cG9ydCBNT05JVE9SSU5HX1VTRV9HRU5FVkFfQ09ORklHX1NFUlZJQ0U9dHJ1ZQoKZXhwb3J0IE1PTklUT1JJTkdfVEVOQU5UPSckTE9DQVRJT04nCmV4cG9ydCBNT05JVE9SSU5HX1JPTEU9Z2F0ZXdheQpleHBvcnQgTU9OSVRPUklOR19ST0xFX0lOU1RBTkNFPSckKGhvc3RuYW1lKScKCmV4cG9ydCBNRFNEX01TR1BBQ0tfU09SVF9DT0xVTU5TPTEKRU9GCgojIHNldHRpbmcgTU9OSVRPUklOR19HQ1NfQVVUSF9JRF9UWVBFPUF1dGhLZXlWYXVsdCBzZWVtcyB0byBoYXZlIGNhdXNlZCBtZHNkIG5vdAojIHRvIGhvbm91ciBTU0xfQ0VSVF9GSUxFIGFueSBtb3JlLCBoZWF2ZW4gb25seSBrbm93cyB3aHkuCm1rZGlyIC1wIC91c3IvbGliL3NzbC9jZXJ0cwpjc3BsaXQgLWYgL3Vzci9saWIvc3NsL2NlcnRzL2NlcnQtIC1iICUwM2QucGVtIC9ldGMvcGtpL3Rscy9jZXJ0cy9jYS1idW5kbGUuY3J0IC9eJC8xIHsqfSA+L2Rldi9udWxsCmNfcmVoYXNoIC91c3IvbGliL3NzbC9jZXJ0cwoKIyB3ZSBsZWF2ZSBjbGllbnRJZCBibGFuayBhcyBsb25nIGFzIG9ubHkgMSBtYW5hZ2VkIGlkZW50aXR5IGFzc2lnbmVkIHRvIHZtc3MKIyBpZiB3ZSBoYXZlIG1vcmUgdGhhbiAxLCB3ZSB3aWxsIG5lZWQgdG8gcG9wdWxhdGUgd2l0aCBjbGllbnRJZCB1c2VkIGZvciBvZmYtbm9kZSBzY2FubmluZwpjYXQgPi9ldGMvZGVmYXVsdC92c2Etbm9kZXNjYW4tYWdlbnQuY29uZmlnIDw8RU9GCnsKICAgICJOaWNlIjogMTksCiAgICAiVGltZW91dCI6IDEwODAwLAogICAgIkNsaWVudElkIjogIiIsCiAgICAiVGVuYW50SWQiOiAiJEFaVVJFU0VDUEFDS1ZTQVRFTkFOVElEIiwKICAgICJRdWFseXNTdG9yZUJhc2VVcmwiOiAiJEFaVVJFU0VDUEFDS1FVQUxZU1VSTCIsCiAgICAiUHJvY2Vzc1RpbWVvdXQiOiAzMDAsCiAgICAiQ29tbWFuZERlbGF5IjogMAogIH0KRU9GCgplY2hvICJlbmFibGluZyBhcm8gc2VydmljZXMiCmZvciBzZXJ2aWNlIGluIGFyby1nYXRld2F5IGF1b21zIGF6c2VjZCBhenNlY21vbmQgbWRzZCBtZG0gY2hyb255ZCBmbHVlbnRiaXQ7IGRvCiAgc3lzdGVtY3RsIGVuYWJsZSAkc2VydmljZS5zZXJ2aWNlCmRvbmUKCmZvciBzY2FuIGluIGJhc2VsaW5lIGNsYW1hdiBzb2Z0d2FyZTsgZG8KICAvdXNyL2xvY2FsL2Jpbi9henNlY2QgY29uZmlnIC1zICRzY2FuIC1kIFAxRApkb25lCgplY2hvICJyZWJvb3RpbmciCnJlc3RvcmVjb24gLVJGIC92YXIvbG9nLyoKKHNsZWVwIDMwOyByZWJvb3QpICYK')))]" } } } diff --git a/pkg/deploy/assets/rp-development-predeploy.json b/pkg/deploy/assets/rp-development-predeploy.json index 5d3152ec3e6..02a15e88af4 100644 --- a/pkg/deploy/assets/rp-development-predeploy.json +++ b/pkg/deploy/assets/rp-development-predeploy.json @@ -187,47 +187,6 @@ }, "apiVersion": "2019-09-01" }, - { - "name": "[concat(parameters('keyvaultPrefix'), '-dbt')]", - "type": "Microsoft.KeyVault/vaults", - "location": "[resourceGroup().location]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": [ - { - "tenantId": "[subscription().tenantId]", - "objectId": "[parameters('rpServicePrincipalId')]", - "permissions": { - "secrets": [ - "get" - ] - } - }, - { - "tenantId": "[subscription().tenantId]", - "objectId": "[parameters('adminObjectId')]", - "permissions": { - "secrets": [ - "set", - "list" - ], - "certificates": [ - "delete", - "get", - "import", - "list" - ] - } - } - ], - "enableSoftDelete": true - }, - "apiVersion": "2019-09-01" - }, { "name": "[concat(parameters('keyvaultPrefix'), '-por')]", "type": "Microsoft.KeyVault/vaults", diff --git a/pkg/deploy/assets/rp-production-parameters.json b/pkg/deploy/assets/rp-production-parameters.json index 9e40f9398b6..97740b4f602 100644 --- a/pkg/deploy/assets/rp-production-parameters.json +++ b/pkg/deploy/assets/rp-production-parameters.json @@ -63,9 +63,6 @@ "databaseAccountName": { "value": "" }, - "dbtokenClientId": { - "value": "" - }, "disableCosmosDBFirewall": { "value": false }, diff --git a/pkg/deploy/assets/rp-production-predeploy-parameters.json b/pkg/deploy/assets/rp-production-predeploy-parameters.json index 088062ec473..586a89f4234 100644 --- a/pkg/deploy/assets/rp-production-predeploy-parameters.json +++ b/pkg/deploy/assets/rp-production-predeploy-parameters.json @@ -8,9 +8,6 @@ "extraClusterKeyvaultAccessPolicies": { "value": [] }, - "extraDBTokenKeyvaultAccessPolicies": { - "value": [] - }, "extraPortalKeyvaultAccessPolicies": { "value": [] }, diff --git a/pkg/deploy/assets/rp-production-predeploy.json b/pkg/deploy/assets/rp-production-predeploy.json index a515bc71474..391f1785801 100644 --- a/pkg/deploy/assets/rp-production-predeploy.json +++ b/pkg/deploy/assets/rp-production-predeploy.json @@ -19,17 +19,6 @@ } } ], - "dbTokenKeyvaultAccessPolicies": [ - { - "tenantId": "[subscription().tenantId]", - "objectId": "[parameters('rpServicePrincipalId')]", - "permissions": { - "secrets": [ - "get" - ] - } - } - ], "portalKeyvaultAccessPolicies": [ { "tenantId": "[subscription().tenantId]", @@ -63,10 +52,6 @@ "type": "array", "defaultValue": [] }, - "extraDBTokenKeyvaultAccessPolicies": { - "type": "array", - "defaultValue": [] - }, "extraPortalKeyvaultAccessPolicies": { "type": "array", "defaultValue": [] @@ -123,32 +108,6 @@ }, "name": "rp_in_geneva" }, - { - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "destinationPortRange": "445", - "sourceAddressPrefix": "10.0.8.0/24", - "destinationAddressPrefix": "*", - "access": "Allow", - "priority": 140, - "direction": "Inbound" - }, - "name": "dbtoken_in_gateway_445" - }, - { - "properties": { - "protocol": "Tcp", - "sourcePortRange": "*", - "destinationPortRange": "8445", - "sourceAddressPrefix": "10.0.8.0/24", - "destinationAddressPrefix": "*", - "access": "Allow", - "priority": 141, - "direction": "Inbound" - }, - "name": "dbtoken_in_gateway_8445" - }, { "properties": { "protocol": "Tcp", @@ -278,21 +237,6 @@ }, "apiVersion": "2019-09-01" }, - { - "name": "[concat(parameters('keyvaultPrefix'), '-dbt')]", - "type": "Microsoft.KeyVault/vaults", - "location": "[resourceGroup().location]", - "properties": { - "tenantId": "[subscription().tenantId]", - "sku": { - "family": "A", - "name": "standard" - }, - "accessPolicies": "[concat(variables('dbTokenKeyvaultAccessPolicies'), parameters('extraDBTokenKeyvaultAccessPolicies'))]", - "enableSoftDelete": true - }, - "apiVersion": "2019-09-01" - }, { "name": "[concat(parameters('keyvaultPrefix'), '-por')]", "type": "Microsoft.KeyVault/vaults", diff --git a/pkg/deploy/assets/rp-production.json b/pkg/deploy/assets/rp-production.json index 7ab169abf4f..fa11f46bee7 100644 --- a/pkg/deploy/assets/rp-production.json +++ b/pkg/deploy/assets/rp-production.json @@ -83,9 +83,6 @@ "databaseAccountName": { "type": "string" }, - "dbtokenClientId": { - "type": "string" - }, "disableCosmosDBFirewall": { "type": "bool", "defaultValue": false @@ -357,64 +354,6 @@ "[resourceId('Microsoft.Network/publicIPAddresses', 'rp-pip')]" ] }, - { - "sku": { - "name": "Standard" - }, - "properties": { - "frontendIPConfigurations": [ - { - "properties": { - "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'rp-vnet', 'rp-subnet')]" - } - }, - "name": "dbtoken-frontend", - "zones": "[if(contains(parameters('nonZonalRegions'),toLower(replace(resourceGroup().location, ' ', ''))),'',pickZones('Microsoft.Network', 'publicIPAddresses', resourceGroup().location, 3))]" - } - ], - "backendAddressPools": [ - { - "name": "rp-backend" - } - ], - "loadBalancingRules": [ - { - "properties": { - "frontendIPConfiguration": { - "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'rp-lb-internal', 'dbtoken-frontend')]" - }, - "backendAddressPool": { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb-internal', 'rp-backend')]" - }, - "probe": { - "id": "[resourceId('Microsoft.Network/loadBalancers/probes', 'rp-lb-internal', 'dbtoken-probe')]" - }, - "protocol": "Tcp", - "loadDistribution": "Default", - "frontendPort": 8445, - "backendPort": 445 - }, - "name": "dbtoken-lbrule" - } - ], - "probes": [ - { - "properties": { - "protocol": "Https", - "port": 445, - "numberOfProbes": 2, - "requestPath": "/healthz/ready" - }, - "name": "dbtoken-probe" - } - ] - }, - "name": "rp-lb-internal", - "type": "Microsoft.Network/loadBalancers", - "location": "[resourceGroup().location]", - "apiVersion": "2020-08-01" - }, { "sku": { "name": "[parameters('vmSize')]", @@ -479,9 +418,6 @@ "loadBalancerBackendAddressPools": [ { "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb', 'rp-backend')]" - }, - { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb-internal', 'rp-backend')]" } ] } @@ -507,7 +443,7 @@ "autoUpgradeMinorVersion": true, "settings": {}, "protectedSettings": { - "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'ACRRESOURCEID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('acrResourceId')),''')\n','ADMINAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('adminApiClientCertCommonName')),''')\n','ARMAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armApiClientCertCommonName')),''')\n','ARMCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armClientId')),''')\n','AZURECLOUDNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureCloudName')),''')\n','AZURESECPACKQUALYSURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackQualysUrl')),''')\n','AZURESECPACKVSATENANTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackVSATenantId')),''')\n','CLUSTERMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdmAccount')),''')\n','CLUSTERMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdAccount')),''')\n','CLUSTERMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdConfigVersion')),''')\n','CLUSTERMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdNamespace')),''')\n','CLUSTERPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterParentDomainName')),''')\n','DATABASEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('databaseAccountName')),''')\n','DBTOKENCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('dbtokenClientId')),''')\n','FLUENTBITIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fluentbitImage')),''')\n','FPCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpClientId')),''')\n','FPSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpServicePrincipalId')),''')\n','GATEWAYDOMAINS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayDomains')),''')\n','GATEWAYRESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayResourceGroupName')),''')\n','GATEWAYSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayServicePrincipalId')),''')\n','KEYVAULTDNSSUFFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultDNSSuffix')),''')\n','KEYVAULTPREFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultPrefix')),''')\n','MDMFRONTENDURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdmFrontendUrl')),''')\n','MDSDENVIRONMENT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdsdEnvironment')),''')\n','PORTALACCESSGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalAccessGroupIds')),''')\n','PORTALCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalClientId')),''')\n','PORTALELEVATEDGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalElevatedGroupIds')),''')\n','RPFEATURES=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpFeatures')),''')\n','RPIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpImage')),''')\n','RPMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdmAccount')),''')\n','RPMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdAccount')),''')\n','RPMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdConfigVersion')),''')\n','RPMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdNamespace')),''')\n','RPPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpParentDomainName')),''')\n','CLUSTERSINSTALLVIAHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersInstallViaHive')),''')\n','CLUSTERSADOPTBYHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersAdoptByHive')),''')\n','CLUSTERDEFAULTINSTALLERPULLSPEC=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterDefaultInstallerPullspec')),''')\n','USECHECKACCESS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('useCheckAccess')),''')\n','ADMINAPICABUNDLE=''',parameters('adminApiCaBundle'),'''\n','ARMAPICABUNDLE=''',parameters('armApiCaBundle'),'''\n','MDMIMAGE=''/distroless/genevamdm:2.2024.517.533-b73893-20240522t0954@sha256:939df9d7b6660874697f8ebed1fe56504f86d92f99801a9dc6fd98e9176d3f75''\n','LOCATION=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().location),''')\n','SUBSCRIPTIONID=$(base64 -d \u003c\u003c\u003c''',base64(subscription().subscriptionId),''')\n','RESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().name),''')\n','\n',base64ToString('IyEvYmluL2Jhc2gKCmVjaG8gInNldHRpbmcgc3NoIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uIgojIFdlIG5lZWQgdG8gbWFudWFsbHkgc2V0IFBhc3N3b3JkQXV0aGVudGljYXRpb24gdG8gdHJ1ZSBpbiBvcmRlciBmb3IgdGhlIFZNU1MgQWNjZXNzIEpJVCB0byB3b3JrCnNlZCAtaSAncy9QYXNzd29yZEF1dGhlbnRpY2F0aW9uIG5vL1Bhc3N3b3JkQXV0aGVudGljYXRpb24geWVzL2cnIC9ldGMvc3NoL3NzaGRfY29uZmlnCnN5c3RlbWN0bCByZWxvYWQgc3NoZC5zZXJ2aWNlCgojQWRkaW5nIHJldHJ5IGxvZ2ljIHRvIHl1bSBjb21tYW5kcyBpbiBvcmRlciB0byBhdm9pZCBzdGFsbGluZyBvdXQgb24gcmVzb3VyY2UgbG9ja3MKZWNobyAicnVubmluZyBSSFVJIGZpeCIKZm9yIGF0dGVtcHQgaW4gezEuLjYwfTsgZG8KICB5dW0gdXBkYXRlIC15IC0tZGlzYWJsZXJlcG89JyonIC0tZW5hYmxlcmVwbz0ncmh1aS1taWNyb3NvZnQtYXp1cmUqJyAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAicnVubmluZyB5dW0gdXBkYXRlIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlIC0tYWxsb3dlcmFzaW5nICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgplY2hvICJleHRlbmRpbmcgcGFydGl0aW9uIHRhYmxlIgojIExpbnV4IGJsb2NrIGRldmljZXMgYXJlIGluY29uc2lzdGVudGx5IG5hbWVkCiMgaXQncyBkaWZmaWN1bHQgdG8gdGllIHRoZSBsdm0gcHYgdG8gdGhlIHBoeXNpY2FsIGRpc2sgdXNpbmcgL2Rldi9kaXNrIGZpbGVzLCB3aGljaCBpcyB3aHkgbHZzIGlzIHVzZWQgaGVyZQpwaHlzaWNhbERpc2s9IiQobHZzIC1vIGRldmljZXMgLWEgfCBoZWFkIC1uMiB8IHRhaWwgLW4xIHwgY3V0IC1kICcgJyAtZiAzIHwgY3V0IC1kIFwoIC1mIDEgfCB0ciAtZCAnWzpkaWdpdDpdJykiCmdyb3dwYXJ0ICIkcGh5c2ljYWxEaXNrIiAyCgplY2hvICJleHRlbmRpbmcgZmlsZXN5c3RlbXMiCmx2ZXh0ZW5kIC1sICsyMCVGUkVFIC9kZXYvcm9vdHZnL3Jvb3Rsdgp4ZnNfZ3Jvd2ZzIC8KCmx2ZXh0ZW5kIC1sICsxMDAlRlJFRSAvZGV2L3Jvb3R2Zy92YXJsdgp4ZnNfZ3Jvd2ZzIC92YXIKCmVjaG8gImltcG9ydGluZyBycG0gcmVwb3NpdG9yaWVzIgpycG0gLS1pbXBvcnQgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9SUE0tR1BHLUtFWS1FUEVMLTgKcnBtIC0taW1wb3J0IGh0dHBzOi8vcGFja2FnZXMubWljcm9zb2Z0LmNvbS9rZXlzL21pY3Jvc29mdC5hc2MKCmZvciBhdHRlbXB0IGluIHsxLi42MH07IGRvCiAgeXVtIC15IGluc3RhbGwgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9lcGVsLXJlbGVhc2UtbGF0ZXN0LTgubm9hcmNoLnJwbSAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAiY29uZmlndXJpbmcgbG9ncm90YXRlIgpjYXQgPi9ldGMvbG9ncm90YXRlLmNvbmYgPDwnRU9GJwojIHNlZSAibWFuIGxvZ3JvdGF0ZSIgZm9yIGRldGFpbHMKIyByb3RhdGUgbG9nIGZpbGVzIHdlZWtseQp3ZWVrbHkKCiMga2VlcCAyIHdlZWtzIHdvcnRoIG9mIGJhY2tsb2dzCnJvdGF0ZSAyCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBsb2cgZmlsZXMgYWZ0ZXIgcm90YXRpbmcgb2xkIG9uZXMKY3JlYXRlCgojIHVzZSBkYXRlIGFzIGEgc3VmZml4IG9mIHRoZSByb3RhdGVkIGZpbGUKZGF0ZWV4dAoKIyB1bmNvbW1lbnQgdGhpcyBpZiB5b3Ugd2FudCB5b3VyIGxvZyBmaWxlcyBjb21wcmVzc2VkCmNvbXByZXNzCgojIFJQTSBwYWNrYWdlcyBkcm9wIGxvZyByb3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIHRoaXMgZGlyZWN0b3J5CmluY2x1ZGUgL2V0Yy9sb2dyb3RhdGUuZAoKIyBubyBwYWNrYWdlcyBvd24gd3RtcCBhbmQgYnRtcCAtLSB3ZSdsbCByb3RhdGUgdGhlbSBoZXJlCi92YXIvbG9nL3d0bXAgewogICAgbW9udGhseQogICAgY3JlYXRlIDA2NjQgcm9vdCB1dG1wCiAgICAgICAgbWluc2l6ZSAxTQogICAgcm90YXRlIDEKfQoKL3Zhci9sb2cvYnRtcCB7CiAgICBtaXNzaW5nb2sKICAgIG1vbnRobHkKICAgIGNyZWF0ZSAwNjAwIHJvb3QgdXRtcAogICAgcm90YXRlIDEKfQpFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIHl1bSByZXBvc2l0b3J5IGFuZCBydW5uaW5nIHl1bSB1cGRhdGUiCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwoKW2F6dXJlY29yZV0KbmFtZT1henVyZWNvcmUKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmVjb3JlCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPW5vCkVPRgoKc2VtYW5hZ2UgZmNvbnRleHQgLWEgLXQgdmFyX2xvZ190ICIvdmFyL2xvZy9qb3VybmFsKC8uKik/Igpta2RpciAtcCAvdmFyL2xvZy9qb3VybmFsCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwp5dW0gLXkgaW5zdGFsbCBjbGFtYXYgYXpzZWMtY2xhbWF2IGF6c2VjLW1vbml0b3IgYXp1cmUtY2xpIGF6dXJlLW1kc2QgYXp1cmUtc2VjdXJpdHkgcG9kbWFuIHBvZG1hbi1kb2NrZXIgb3BlbnNzbC1wZXJsIHB5dGhvbjMgJiYgYnJlYWsKICAjIGhhY2sgLSB3ZSBhcmUgaW5zdGFsbGluZyBweXRob24zIG9uIGhvc3RzIGR1ZSB0byBhbiBpc3N1ZSB3aXRoIEF6dXJlIExpbnV4IEV4dGVuc2lvbnMgaHR0cHM6Ly9naXRodWIuY29tL0F6dXJlL2F6dXJlLWxpbnV4LWV4dGVuc2lvbnMvcHVsbC8xNTA1CiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgojIGh0dHBzOi8vYWNjZXNzLnJlZGhhdC5jb20vc2VjdXJpdHkvY3ZlL2N2ZS0yMDIwLTEzNDAxCmVjaG8gImFwcGx5aW5nIGZpcmV3YWxsIHJ1bGVzIgpjYXQgPi9ldGMvc3lzY3RsLmQvMDItZGlzYWJsZS1hY2NlcHQtcmEuY29uZiA8PCdFT0YnCm5ldC5pcHY2LmNvbmYuYWxsLmFjY2VwdF9yYT0wCkVPRgoKY2F0ID4vZXRjL3N5c2N0bC5kLzAxLWRpc2FibGUtY29yZS5jb25mIDw8J0VPRicKa2VybmVsLmNvcmVfcGF0dGVybiA9IHwvYmluL3RydWUKRU9GCnN5c2N0bCAtLXN5c3RlbQoKZmlyZXdhbGwtY21kIC0tYWRkLXBvcnQ9NDQzL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD00NDQvdGNwIC0tcGVybWFuZW50CmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTQ0NS90Y3AgLS1wZXJtYW5lbnQKZmlyZXdhbGwtY21kIC0tYWRkLXBvcnQ9MjIyMi90Y3AgLS1wZXJtYW5lbnQKCmV4cG9ydCBBWlVSRV9DTE9VRF9OQU1FPSRBWlVSRUNMT1VETkFNRQoKZWNobyAibG9nZ2luZyBpbnRvIHByb2QgYWNyIgpheiBsb2dpbiAtaSAtLWFsbG93LW5vLXN1YnNjcmlwdGlvbnMKCiMgU3VwcHJlc3MgZW11bGF0aW9uIG91dHB1dCBmb3IgcG9kbWFuIGluc3RlYWQgb2YgZG9ja2VyIGZvciBheiBhY3IgY29tcGF0YWJpbGl0eQpta2RpciAtcCAvZXRjL2NvbnRhaW5lcnMvCnRvdWNoIC9ldGMvY29udGFpbmVycy9ub2RvY2tlcgoKbWtkaXIgLXAgL3Jvb3QvLmRvY2tlcgpSRUdJU1RSWV9BVVRIX0ZJTEU9L3Jvb3QvLmRvY2tlci9jb25maWcuanNvbiBheiBhY3IgbG9naW4gLS1uYW1lICIkKHNlZCAtZSAnc3wuKi98fCcgPDw8IiRBQ1JSRVNPVVJDRUlEIikiCgpNRE1JTUFHRT0iJHtSUElNQUdFJSUvKn0vJHtNRE1JTUFHRSMqL30iCmRvY2tlciBwdWxsICIkTURNSU1BR0UiCmRvY2tlciBwdWxsICIkUlBJTUFHRSIKZG9ja2VyIHB1bGwgIiRGTFVFTlRCSVRJTUFHRSIKCmF6IGxvZ291dAoKZWNobyAiY29uZmlndXJpbmcgZmx1ZW50Yml0IHNlcnZpY2UiCm1rZGlyIC1wIC9ldGMvZmx1ZW50Yml0Lwpta2RpciAtcCAvdmFyL2xpYi9mbHVlbnQKCmNhdCA+L2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmYgPDwnRU9GJwpbSU5QVVRdCglOYW1lIHN5c3RlbWQKCVRhZyBqb3VybmFsZAoJU3lzdGVtZF9GaWx0ZXIgX0NPTU09YXJvCglEQiAvdmFyL2xpYi9mbHVlbnQvam91cm5hbGRiCgpbRklMVEVSXQoJTmFtZSBtb2RpZnkKCU1hdGNoIGpvdXJuYWxkCglSZW1vdmVfd2lsZGNhcmQgXwoJUmVtb3ZlIFRJTUVTVEFNUAoKW0ZJTFRFUl0KCU5hbWUgcmV3cml0ZV90YWcKCU1hdGNoIGpvdXJuYWxkCglSdWxlICRMT0dLSU5EIGFzeW5jcW9zIGFzeW5jcW9zIHRydWUKCltGSUxURVJdCglOYW1lIG1vZGlmeQoJTWF0Y2ggYXN5bmNxb3MKCVJlbW92ZSBDTElFTlRfUFJJTkNJUEFMX05BTUUKCVJlbW92ZSBGSUxFCglSZW1vdmUgQ09NUE9ORU5UCgpbRklMVEVSXQoJTmFtZSByZXdyaXRlX3RhZwoJTWF0Y2ggam91cm5hbGQKCVJ1bGUgJExPR0tJTkQgaWZ4YXVkaXQgaWZ4YXVkaXQgZmFsc2UKCltPVVRQVVRdCglOYW1lIGZvcndhcmQKCU1hdGNoICoKCVBvcnQgMjkyMzAKRU9GCgplY2hvICJGTFVFTlRCSVRJTUFHRT0kRkxVRU5UQklUSU1BR0UiID4vZXRjL3N5c2NvbmZpZy9mbHVlbnRiaXQKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9mbHVlbnRiaXQuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0ClN0YXJ0TGltaXRJbnRlcnZhbFNlYz0wCgpbU2VydmljZV0KUmVzdGFydFNlYz0xcwpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvZmx1ZW50Yml0CkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLXNlY3VyaXR5LW9wdCBsYWJlbD1kaXNhYmxlIFwKICAtLWVudHJ5cG9pbnQgL29wdC90ZC1hZ2VudC1iaXQvYmluL3RkLWFnZW50LWJpdCBcCiAgLS1uZXQ9aG9zdCBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC12IC9ldGMvZmx1ZW50Yml0L2ZsdWVudGJpdC5jb25mOi9ldGMvZmx1ZW50Yml0L2ZsdWVudGJpdC5jb25mIFwKICAtdiAvdmFyL2xpYi9mbHVlbnQ6L3Zhci9saWIvZmx1ZW50OnogXAogIC12IC92YXIvbG9nL2pvdXJuYWw6L3Zhci9sb2cvam91cm5hbDpybyBcCiAgLXYgL2V0Yy9tYWNoaW5lLWlkOi9ldGMvbWFjaGluZS1pZDpybyBcCiAgJEZMVUVOVEJJVElNQUdFIFwKICAtYyAvZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZgoKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgJU4KUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz01ClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgpta2RpciAvZXRjL2Fyby1ycApiYXNlNjQgLWQgPDw8IiRBRE1JTkFQSUNBQlVORExFIiA+L2V0Yy9hcm8tcnAvYWRtaW4tY2EtYnVuZGxlLnBlbQppZiBbWyAtbiAiJEFSTUFQSUNBQlVORExFIiBdXTsgdGhlbgogIGJhc2U2NCAtZCA8PDwiJEFSTUFQSUNBQlVORExFIiA+L2V0Yy9hcm8tcnAvYXJtLWNhLWJ1bmRsZS5wZW0KZmkKY2hvd24gLVIgMTAwMDoxMDAwIC9ldGMvYXJvLXJwCgplY2hvICJjb25maWd1cmluZyBtZG0gc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9tZG0gPDxFT0YKTURNRlJPTlRFTkRVUkw9JyRNRE1GUk9OVEVORFVSTCcKTURNSU1BR0U9JyRNRE1JTUFHRScKTURNU09VUkNFRU5WSVJPTk1FTlQ9JyRMT0NBVElPTicKTURNU09VUkNFUk9MRT1ycApNRE1TT1VSQ0VST0xFSU5TVEFOQ0U9JyQoaG9zdG5hbWUpJwpFT0YKCm1rZGlyIC92YXIvZXR3CmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZG0uc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL21kbQpFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1lbnRyeXBvaW50IC91c3Ivc2Jpbi9NZXRyaWNzRXh0ZW5zaW9uIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLW0gMmcgXAogIC12IC9ldGMvbWRtLnBlbTovZXRjL21kbS5wZW0gXAogIC12IC92YXIvZXR3Oi92YXIvZXR3OnogXAogICRNRE1JTUFHRSBcCiAgLUNlcnRGaWxlIC9ldGMvbWRtLnBlbSBcCiAgLUZyb250RW5kVXJsICRNRE1GUk9OVEVORFVSTCBcCiAgLUxvZ2dlciBDb25zb2xlIFwKICAtTG9nTGV2ZWwgV2FybmluZyBcCiAgLVByaXZhdGVLZXlGaWxlIC9ldGMvbWRtLnBlbSBcCiAgLVNvdXJjZUVudmlyb25tZW50ICRNRE1TT1VSQ0VFTlZJUk9OTUVOVCBcCiAgLVNvdXJjZVJvbGUgJE1ETVNPVVJDRVJPTEUgXAogIC1Tb3VyY2VSb2xlSW5zdGFuY2UgJE1ETVNPVVJDRVJPTEVJTlNUQU5DRQpFeGVjU3RvcD0vdXNyL2Jpbi9kb2NrZXIgc3RvcCAlTgpSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIGFyby1ycCBzZXJ2aWNlIgpjYXQgPi9ldGMvc3lzY29uZmlnL2Fyby1ycCA8PEVPRgpBQ1JfUkVTT1VSQ0VfSUQ9JyRBQ1JSRVNPVVJDRUlEJwpBRE1JTl9BUElfQ0xJRU5UX0NFUlRfQ09NTU9OX05BTUU9JyRBRE1JTkFQSUNMSUVOVENFUlRDT01NT05OQU1FJwpBUk1fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FPSckQVJNQVBJQ0xJRU5UQ0VSVENPTU1PTk5BTUUnCkFaVVJFX0FSTV9DTElFTlRfSUQ9JyRBUk1DTElFTlRJRCcKQVpVUkVfRlBfQ0xJRU5UX0lEPSckRlBDTElFTlRJRCcKQVpVUkVfRlBfU0VSVklDRV9QUklOQ0lQQUxfSUQ9JyRGUFNFUlZJQ0VQUklOQ0lQQUxJRCcKQ0xVU1RFUl9NRE1fQUNDT1VOVD0nJENMVVNURVJNRE1BQ0NPVU5UJwpDTFVTVEVSX01ETV9OQU1FU1BBQ0U9UlAKQ0xVU1RFUl9NRFNEX0FDQ09VTlQ9JyRDTFVTVEVSTURTREFDQ09VTlQnCkNMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTj0nJENMVVNURVJNRFNEQ09ORklHVkVSU0lPTicKQ0xVU1RFUl9NRFNEX05BTUVTUEFDRT0nJENMVVNURVJNRFNETkFNRVNQQUNFJwpEQVRBQkFTRV9BQ0NPVU5UX05BTUU9JyREQVRBQkFTRUFDQ09VTlROQU1FJwpET01BSU5fTkFNRT0nJExPQ0FUSU9OLiRDTFVTVEVSUEFSRU5URE9NQUlOTkFNRScKR0FURVdBWV9ET01BSU5TPSckR0FURVdBWURPTUFJTlMnCkdBVEVXQVlfUkVTT1VSQ0VHUk9VUD0nJEdBVEVXQVlSRVNPVVJDRUdST1VQTkFNRScKS0VZVkFVTFRfUFJFRklYPSckS0VZVkFVTFRQUkVGSVgnCk1ETV9BQ0NPVU5UPSckUlBNRE1BQ0NPVU5UJwpNRE1fTkFNRVNQQUNFPVJQCk1EU0RfRU5WSVJPTk1FTlQ9JyRNRFNERU5WSVJPTk1FTlQnClJQX0ZFQVRVUkVTPSckUlBGRUFUVVJFUycKUlBJTUFHRT0nJFJQSU1BR0UnCkFST19JTlNUQUxMX1ZJQV9ISVZFPSckQ0xVU1RFUlNJTlNUQUxMVklBSElWRScKQVJPX0hJVkVfREVGQVVMVF9JTlNUQUxMRVJfUFVMTFNQRUM9JyRDTFVTVEVSREVGQVVMVElOU1RBTExFUlBVTExTUEVDJwpBUk9fQURPUFRfQllfSElWRT0nJENMVVNURVJTQURPUFRCWUhJVkUnClVTRV9DSEVDS0FDQ0VTUz0nJFVTRUNIRUNLQUNDRVNTJwpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9hcm8tcnAuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2Fyby1ycApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC1lIEFDUl9SRVNPVVJDRV9JRCBcCiAgLWUgQURNSU5fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FIFwKICAtZSBBUk1fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FIFwKICAtZSBBWlVSRV9BUk1fQ0xJRU5UX0lEIFwKICAtZSBBWlVSRV9GUF9DTElFTlRfSUQgXAogIC1lIENMVVNURVJfTURNX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURNX05BTUVTUEFDRSBcCiAgLWUgQ0xVU1RFUl9NRFNEX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTiBcCiAgLWUgQ0xVU1RFUl9NRFNEX05BTUVTUEFDRSBcCiAgLWUgREFUQUJBU0VfQUNDT1VOVF9OQU1FIFwKICAtZSBET01BSU5fTkFNRSBcCiAgLWUgR0FURVdBWV9ET01BSU5TIFwKICAtZSBHQVRFV0FZX1JFU09VUkNFR1JPVVAgXAogIC1lIEtFWVZBVUxUX1BSRUZJWCBcCiAgLWUgTURNX0FDQ09VTlQgXAogIC1lIE1ETV9OQU1FU1BBQ0UgXAogIC1lIE1EU0RfRU5WSVJPTk1FTlQgXAogIC1lIFJQX0ZFQVRVUkVTIFwKICAtZSBBUk9fSU5TVEFMTF9WSUFfSElWRSBcCiAgLWUgQVJPX0hJVkVfREVGQVVMVF9JTlNUQUxMRVJfUFVMTFNQRUMgXAogIC1lIEFST19BRE9QVF9CWV9ISVZFIFwKICAtZSBVU0VfQ0hFQ0tBQ0NFU1MgXAogIC1tIDJnIFwKICAtcCA0NDM6ODQ0MyBcCiAgLXYgL2V0Yy9hcm8tcnA6L2V0Yy9hcm8tcnAgXAogIC12IC9ydW4vc3lzdGVtZC9qb3VybmFsOi9ydW4vc3lzdGVtZC9qb3VybmFsIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkUlBJTUFHRSBcCiAgcnAKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgLXQgMzYwMCAlTgpUaW1lb3V0U3RvcFNlYz0zNjAwClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKZWNobyAiY29uZmlndXJpbmcgYXJvLWRidG9rZW4gc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9hcm8tZGJ0b2tlbiA8PEVPRgpEQVRBQkFTRV9BQ0NPVU5UX05BTUU9JyREQVRBQkFTRUFDQ09VTlROQU1FJwpBWlVSRV9EQlRPS0VOX0NMSUVOVF9JRD0nJERCVE9LRU5DTElFTlRJRCcKQVpVUkVfR0FURVdBWV9TRVJWSUNFX1BSSU5DSVBBTF9JRD0nJEdBVEVXQVlTRVJWSUNFUFJJTkNJUEFMSUQnCktFWVZBVUxUX1BSRUZJWD0nJEtFWVZBVUxUUFJFRklYJwpNRE1fQUNDT1VOVD0nJFJQTURNQUNDT1VOVCcKTURNX05BTUVTUEFDRT1EQlRva2VuClJQSU1BR0U9JyRSUElNQUdFJwpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9hcm8tZGJ0b2tlbi5zZXJ2aWNlIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvYXJvLWRidG9rZW4KRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtZSBBWlVSRV9HQVRFV0FZX1NFUlZJQ0VfUFJJTkNJUEFMX0lEIFwKICAtZSBEQVRBQkFTRV9BQ0NPVU5UX05BTUUgXAogIC1lIEFaVVJFX0RCVE9LRU5fQ0xJRU5UX0lEIFwKICAtZSBLRVlWQVVMVF9QUkVGSVggXAogIC1lIE1ETV9BQ0NPVU5UIFwKICAtZSBNRE1fTkFNRVNQQUNFIFwKICAtbSAyZyBcCiAgLXAgNDQ1Ojg0NDUgXAogIC12IC9ydW4vc3lzdGVtZC9qb3VybmFsOi9ydW4vc3lzdGVtZC9qb3VybmFsIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkUlBJTUFHRSBcCiAgZGJ0b2tlbgpFeGVjU3RvcD0vdXNyL2Jpbi9kb2NrZXIgc3RvcCAtdCAzNjAwICVOClRpbWVvdXRTdG9wU2VjPTM2MDAKUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgojIERPTUFJTl9OQU1FLCBDTFVTVEVSX01EU0RfQUNDT1VOVCwgQ0xVU1RFUl9NRFNEX0NPTkZJR19WRVJTSU9OLCBHQVRFV0FZX0RPTUFJTlMsIEdBVEVXQVlfUkVTT1VSQ0VHUk9VUCwgTURTRF9FTlZJUk9OTUVOVCBDTFVTVEVSX01EU0RfTkFNRVNQQUNFCiMgYXJlIG5vdCB1c2VkLCBidXQgY2FuJ3QgZWFzaWx5IGJlIHJlZmFjdG9yZWQgb3V0LiBTaG91bGQgYmUgcmV2aXNpdGVkIGluIHRoZSBmdXR1cmUuCmVjaG8gImNvbmZpZ3VyaW5nIGFyby1tb25pdG9yIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvYXJvLW1vbml0b3IgPDxFT0YKQVpVUkVfRlBfQ0xJRU5UX0lEPSckRlBDTElFTlRJRCcKRE9NQUlOX05BTUU9JyRMT0NBVElPTi4kQ0xVU1RFUlBBUkVOVERPTUFJTk5BTUUnCkNMVVNURVJfTURTRF9BQ0NPVU5UPSckQ0xVU1RFUk1EU0RBQ0NPVU5UJwpDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT049JyRDTFVTVEVSTURTRENPTkZJR1ZFUlNJT04nCkdBVEVXQVlfRE9NQUlOUz0nJEdBVEVXQVlET01BSU5TJwpHQVRFV0FZX1JFU09VUkNFR1JPVVA9JyRHQVRFV0FZUkVTT1VSQ0VHUk9VUE5BTUUnCk1EU0RfRU5WSVJPTk1FTlQ9JyRNRFNERU5WSVJPTk1FTlQnCkNMVVNURVJfTURTRF9OQU1FU1BBQ0U9JyRDTFVTVEVSTURTRE5BTUVTUEFDRScKQ0xVU1RFUl9NRE1fQUNDT1VOVD0nJENMVVNURVJNRE1BQ0NPVU5UJwpDTFVTVEVSX01ETV9OQU1FU1BBQ0U9QkJNCkRBVEFCQVNFX0FDQ09VTlRfTkFNRT0nJERBVEFCQVNFQUNDT1VOVE5BTUUnCktFWVZBVUxUX1BSRUZJWD0nJEtFWVZBVUxUUFJFRklYJwpNRE1fQUNDT1VOVD0nJFJQTURNQUNDT1VOVCcKTURNX05BTUVTUEFDRT1CQk0KUlBJTUFHRT0nJFJQSU1BR0UnCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Fyby1tb25pdG9yLnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldAoKW1NlcnZpY2VdCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9hcm8tbW9uaXRvcgpFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC1lIEFaVVJFX0ZQX0NMSUVOVF9JRCBcCiAgLWUgRE9NQUlOX05BTUUgXAogIC1lIENMVVNURVJfTURTRF9BQ0NPVU5UIFwKICAtZSBDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT04gXAogIC1lIEdBVEVXQVlfRE9NQUlOUyBcCiAgLWUgR0FURVdBWV9SRVNPVVJDRUdST1VQIFwKICAtZSBNRFNEX0VOVklST05NRU5UIFwKICAtZSBDTFVTVEVSX01EU0RfTkFNRVNQQUNFIFwKICAtZSBDTFVTVEVSX01ETV9BQ0NPVU5UIFwKICAtZSBDTFVTVEVSX01ETV9OQU1FU1BBQ0UgXAogIC1lIERBVEFCQVNFX0FDQ09VTlRfTkFNRSBcCiAgLWUgS0VZVkFVTFRfUFJFRklYIFwKICAtZSBNRE1fQUNDT1VOVCBcCiAgLWUgTURNX05BTUVTUEFDRSBcCiAgLW0gMi41ZyBcCiAgLXYgL3J1bi9zeXN0ZW1kL2pvdXJuYWw6L3J1bi9zeXN0ZW1kL2pvdXJuYWwgXAogIC12IC92YXIvZXR3Oi92YXIvZXR3OnogXAogICRSUElNQUdFIFwKICBtb25pdG9yClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKZWNobyAiY29uZmlndXJpbmcgYXJvLXBvcnRhbCBzZXJ2aWNlIgpjYXQgPi9ldGMvc3lzY29uZmlnL2Fyby1wb3J0YWwgPDxFT0YKQVpVUkVfUE9SVEFMX0FDQ0VTU19HUk9VUF9JRFM9JyRQT1JUQUxBQ0NFU1NHUk9VUElEUycKQVpVUkVfUE9SVEFMX0NMSUVOVF9JRD0nJFBPUlRBTENMSUVOVElEJwpBWlVSRV9QT1JUQUxfRUxFVkFURURfR1JPVVBfSURTPSckUE9SVEFMRUxFVkFURURHUk9VUElEUycKREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKS0VZVkFVTFRfUFJFRklYPSckS0VZVkFVTFRQUkVGSVgnCk1ETV9BQ0NPVU5UPSckUlBNRE1BQ0NPVU5UJwpNRE1fTkFNRVNQQUNFPVBvcnRhbApQT1JUQUxfSE9TVE5BTUU9JyRMT0NBVElPTi5hZG1pbi4kUlBQQVJFTlRET01BSU5OQU1FJwpSUElNQUdFPSckUlBJTUFHRScKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vYXJvLXBvcnRhbC5zZXJ2aWNlIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKU3RhcnRMaW1pdEludGVydmFsPTAKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9zeXNjb25maWcvYXJvLXBvcnRhbApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC1lIEFaVVJFX1BPUlRBTF9BQ0NFU1NfR1JPVVBfSURTIFwKICAtZSBBWlVSRV9QT1JUQUxfQ0xJRU5UX0lEIFwKICAtZSBBWlVSRV9QT1JUQUxfRUxFVkFURURfR1JPVVBfSURTIFwKICAtZSBEQVRBQkFTRV9BQ0NPVU5UX05BTUUgXAogIC1lIEtFWVZBVUxUX1BSRUZJWCBcCiAgLWUgTURNX0FDQ09VTlQgXAogIC1lIE1ETV9OQU1FU1BBQ0UgXAogIC1lIFBPUlRBTF9IT1NUTkFNRSBcCiAgLW0gMmcgXAogIC1wIDQ0NDo4NDQ0IFwKICAtcCAyMjIyOjIyMjIgXAogIC12IC9ydW4vc3lzdGVtZC9qb3VybmFsOi9ydW4vc3lzdGVtZC9qb3VybmFsIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkUlBJTUFHRSBcCiAgcG9ydGFsClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9MQoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKZWNobyAiY29uZmlndXJpbmcgbWRzZCBhbmQgbWRtIHNlcnZpY2VzIgpjaGNvbiAtUiBzeXN0ZW1fdTpvYmplY3Rfcjp2YXJfbG9nX3Q6czAgL3Zhci9vcHQvbWljcm9zb2Z0L2xpbnV4bW9uYWdlbnQKCm1rZGlyIC1wIC92YXIvbGliL3dhYWdlbnQvTWljcm9zb2Z0LkF6dXJlLktleVZhdWx0LlN0b3JlCgpmb3IgdmFyIGluICJtZHNkIiAibWRtIjsgZG8KY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Rvd25sb2FkLSR2YXItY3JlZGVudGlhbHMuc2VydmljZSA8PEVPRgpbVW5pdF0KRGVzY3JpcHRpb249UGVyaW9kaWMgJHZhciBjcmVkZW50aWFscyByZWZyZXNoCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4ZWNTdGFydD0vdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCAkdmFyCkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL2Rvd25sb2FkLSR2YXItY3JlZGVudGlhbHMudGltZXIgPDxFT0YKW1VuaXRdCkRlc2NyaXB0aW9uPVBlcmlvZGljICR2YXIgY3JlZGVudGlhbHMgcmVmcmVzaApBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbVGltZXJdCk9uQm9vdFNlYz0wbWluCk9uQ2FsZW5kYXI9MC8xMjowMDowMApBY2N1cmFjeVNlYz01cwoKW0luc3RhbGxdCldhbnRlZEJ5PXRpbWVycy50YXJnZXQKRU9GCmRvbmUKCmNhdCA+L3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggPDxFT0YKIyEvYmluL2Jhc2gKc2V0IC1ldQoKQ09NUE9ORU5UPSJcJDEiCmVjaG8gIkRvd25sb2FkIFwkQ09NUE9ORU5UIGNyZWRlbnRpYWxzIgoKVEVNUF9ESVI9XCQobWt0ZW1wIC1kKQpleHBvcnQgQVpVUkVfQ09ORklHX0RJUj1cJChta3RlbXAgLWQpCgplY2hvICJMb2dnaW5nIGludG8gQXp1cmUuLi4iClJFVFJJRVM9Mwp3aGlsZSBbICJcJFJFVFJJRVMiIC1ndCAwIF07IGRvCiAgICBpZiBheiBsb2dpbiAtaSAtLWFsbG93LW5vLXN1YnNjcmlwdGlvbnMKICAgIHRoZW4KICAgICAgICBlY2hvICJheiBsb2dpbiBzdWNjZXNzZnVsIgogICAgICAgIGJyZWFrCiAgICBlbHNlCiAgICAgICAgZWNobyAiYXogbG9naW4gZmFpbGVkLiBSZXRyeWluZy4uLiIKICAgICAgICBsZXQgUkVUUklFUy09MQogICAgICAgIHNsZWVwIDUKICAgIGZpCmRvbmUKCnRyYXAgImNsZWFudXAiIEVYSVQKCmNsZWFudXAoKSB7CiAgYXogbG9nb3V0CiAgW1sgIlwkVEVNUF9ESVIiID1+IC90bXAvLisgXV0gJiYgcm0gLXJmIFwkVEVNUF9ESVIKICBbWyAiXCRBWlVSRV9DT05GSUdfRElSIiA9fiAvdG1wLy4rIF1dICYmIHJtIC1yZiBcJEFaVVJFX0NPTkZJR19ESVIKfQoKaWYgWyAiXCRDT01QT05FTlQiID0gIm1kbSIgXTsgdGhlbgogIENVUlJFTlRfQ0VSVF9GSUxFPSIvZXRjL21kbS5wZW0iCmVsaWYgWyAiXCRDT01QT05FTlQiID0gIm1kc2QiIF07IHRoZW4KICBDVVJSRU5UX0NFUlRfRklMRT0iL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUvbWRzZC5wZW0iCmVsc2UKICBlY2hvIEludmFsaWQgdXNhZ2UgJiYgZXhpdCAxCmZpCgpTRUNSRVRfTkFNRT0icnAtXCR7Q09NUE9ORU5UfSIKTkVXX0NFUlRfRklMRT0iXCRURU1QX0RJUi9cJENPTVBPTkVOVC5wZW0iCmZvciBhdHRlbXB0IGluIHsxLi41fTsgZG8KICBheiBrZXl2YXVsdCBzZWNyZXQgZG93bmxvYWQgLS1maWxlIFwkTkVXX0NFUlRfRklMRSAtLWlkICJodHRwczovLyRLRVlWQVVMVFBSRUZJWC1zdmMuJEtFWVZBVUxURE5TU1VGRklYL3NlY3JldHMvXCRTRUNSRVRfTkFNRSIgJiYgYnJlYWsKICBpZiBbWyBcJGF0dGVtcHQgLWx0IDUgXV07IHRoZW4gc2xlZXAgMTA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgppZiBbIC1mIFwkTkVXX0NFUlRfRklMRSBdOyB0aGVuCiAgaWYgWyAiXCRDT01QT05FTlQiID0gIm1kc2QiIF07IHRoZW4KICAgIGNob3duIHN5c2xvZzpzeXNsb2cgXCRORVdfQ0VSVF9GSUxFCiAgZWxzZQogICAgc2VkIC1pIC1uZSAnMSwvRU5EIENFUlRJRklDQVRFLyBwJyBcJE5FV19DRVJUX0ZJTEUKICBmaQoKICBuZXdfY2VydF9zbj0iXCQob3BlbnNzbCB4NTA5IC1pbiAiXCRORVdfQ0VSVF9GSUxFIiAtbm9vdXQgLXNlcmlhbCB8IGF3ayAtRj0gJ3twcmludCBcJDJ9JykiCiAgY3VycmVudF9jZXJ0X3NuPSJcJChvcGVuc3NsIHg1MDkgLWluICJcJENVUlJFTlRfQ0VSVF9GSUxFIiAtbm9vdXQgLXNlcmlhbCB8IGF3ayAtRj0gJ3twcmludCBcJDJ9JykiCiAgaWYgW1sgISAteiBcJG5ld19jZXJ0X3NuIF1dICYmIFtbIFwkbmV3X2NlcnRfc24gIT0gIlwkY3VycmVudF9jZXJ0X3NuIiBdXTsgdGhlbgogICAgZWNobyB1cGRhdGluZyBjZXJ0aWZpY2F0ZSBmb3IgXCRDT01QT05FTlQKICAgIGNobW9kIDA2MDAgXCRORVdfQ0VSVF9GSUxFCiAgICBtdiBcJE5FV19DRVJUX0ZJTEUgXCRDVVJSRU5UX0NFUlRfRklMRQogIGZpCmVsc2UKICBlY2hvIEZhaWxlZCB0byByZWZyZXNoIGNlcnRpZmljYXRlIGZvciBcJENPTVBPTkVOVCAmJiBleGl0IDEKZmkKRU9GCgpjaG1vZCB1K3ggL3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2gKCnN5c3RlbWN0bCBlbmFibGUgZG93bmxvYWQtbWRzZC1jcmVkZW50aWFscy50aW1lcgpzeXN0ZW1jdGwgZW5hYmxlIGRvd25sb2FkLW1kbS1jcmVkZW50aWFscy50aW1lcgoKL3Vzci9sb2NhbC9iaW4vZG93bmxvYWQtY3JlZGVudGlhbHMuc2ggbWRzZAovdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCBtZG0KTURTRENFUlRJRklDQVRFU0FOPSQob3BlbnNzbCB4NTA5IC1pbiAvdmFyL2xpYi93YWFnZW50L01pY3Jvc29mdC5BenVyZS5LZXlWYXVsdC5TdG9yZS9tZHNkLnBlbSAtbm9vdXQgLXN1YmplY3QgfCBzZWQgLWUgJ3MvLipDTiA9IC8vJykKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS93YXRjaC1tZG0tY3JlZGVudGlhbHMuc2VydmljZSA8PEVPRgpbVW5pdF0KRGVzY3JpcHRpb249V2F0Y2ggZm9yIGNoYW5nZXMgaW4gbWRtLnBlbSBhbmQgcmVzdGFydHMgdGhlIG1kbSBzZXJ2aWNlCgpbU2VydmljZV0KVHlwZT1vbmVzaG90CkV4ZWNTdGFydD0vdXNyL2Jpbi9zeXN0ZW1jdGwgcmVzdGFydCBtZG0uc2VydmljZQoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL3dhdGNoLW1kbS1jcmVkZW50aWFscy5wYXRoIDw8RU9GCltQYXRoXQpQYXRoTW9kaWZpZWQ9L2V0Yy9tZG0ucGVtCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgpzeXN0ZW1jdGwgZW5hYmxlIHdhdGNoLW1kbS1jcmVkZW50aWFscy5wYXRoCnN5c3RlbWN0bCBzdGFydCB3YXRjaC1tZG0tY3JlZGVudGlhbHMucGF0aAoKbWtkaXIgL2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZHNkLnNlcnZpY2UuZApjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vbWRzZC5zZXJ2aWNlLmQvb3ZlcnJpZGUuY29uZiA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKRU9GCgpjYXQgPi9ldGMvZGVmYXVsdC9tZHNkIDw8RU9GCk1EU0RfUk9MRV9QUkVGSVg9L3Zhci9ydW4vbWRzZC9kZWZhdWx0Ck1EU0RfT1BUSU9OUz0iLUEgLWQgLXIgXCRNRFNEX1JPTEVfUFJFRklYIgoKZXhwb3J0IE1PTklUT1JJTkdfR0NTX0VOVklST05NRU5UPSckTURTREVOVklST05NRU5UJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfQUNDT1VOVD0nJFJQTURTREFDQ09VTlQnCmV4cG9ydCBNT05JVE9SSU5HX0dDU19SRUdJT049JyRMT0NBVElPTicKZXhwb3J0IE1PTklUT1JJTkdfR0NTX0FVVEhfSURfVFlQRT1BdXRoS2V5VmF1bHQKZXhwb3J0IE1PTklUT1JJTkdfR0NTX0FVVEhfSUQ9JyRNRFNEQ0VSVElGSUNBVEVTQU4nCmV4cG9ydCBNT05JVE9SSU5HX0dDU19OQU1FU1BBQ0U9JyRSUE1EU0ROQU1FU1BBQ0UnCmV4cG9ydCBNT05JVE9SSU5HX0NPTkZJR19WRVJTSU9OPSckUlBNRFNEQ09ORklHVkVSU0lPTicKZXhwb3J0IE1PTklUT1JJTkdfVVNFX0dFTkVWQV9DT05GSUdfU0VSVklDRT10cnVlCgpleHBvcnQgTU9OSVRPUklOR19URU5BTlQ9JyRMT0NBVElPTicKZXhwb3J0IE1PTklUT1JJTkdfUk9MRT1ycApleHBvcnQgTU9OSVRPUklOR19ST0xFX0lOU1RBTkNFPSckKGhvc3RuYW1lKScKCmV4cG9ydCBNRFNEX01TR1BBQ0tfU09SVF9DT0xVTU5TPTEKRU9GCgojIHNldHRpbmcgTU9OSVRPUklOR19HQ1NfQVVUSF9JRF9UWVBFPUF1dGhLZXlWYXVsdCBzZWVtcyB0byBoYXZlIGNhdXNlZCBtZHNkIG5vdAojIHRvIGhvbm91ciBTU0xfQ0VSVF9GSUxFIGFueSBtb3JlLCBoZWF2ZW4gb25seSBrbm93cyB3aHkuCm1rZGlyIC1wIC91c3IvbGliL3NzbC9jZXJ0cwpjc3BsaXQgLWYgL3Vzci9saWIvc3NsL2NlcnRzL2NlcnQtIC1iICUwM2QucGVtIC9ldGMvcGtpL3Rscy9jZXJ0cy9jYS1idW5kbGUuY3J0IC9eJC8xIHsqfSA+L2Rldi9udWxsCmNfcmVoYXNoIC91c3IvbGliL3NzbC9jZXJ0cwoKIyB3ZSBsZWF2ZSBjbGllbnRJZCBibGFuayBhcyBsb25nIGFzIG9ubHkgMSBtYW5hZ2VkIGlkZW50aXR5IGFzc2lnbmVkIHRvIHZtc3MKIyBpZiB3ZSBoYXZlIG1vcmUgdGhhbiAxLCB3ZSB3aWxsIG5lZWQgdG8gcG9wdWxhdGUgd2l0aCBjbGllbnRJZCB1c2VkIGZvciBvZmYtbm9kZSBzY2FubmluZwpjYXQgPi9ldGMvZGVmYXVsdC92c2Etbm9kZXNjYW4tYWdlbnQuY29uZmlnIDw8RU9GCnsKICAgICJOaWNlIjogMTksCiAgICAiVGltZW91dCI6IDEwODAwLAogICAgIkNsaWVudElkIjogIiIsCiAgICAiVGVuYW50SWQiOiAiJEFaVVJFU0VDUEFDS1ZTQVRFTkFOVElEIiwKICAgICJRdWFseXNTdG9yZUJhc2VVcmwiOiAiJEFaVVJFU0VDUEFDS1FVQUxZU1VSTCIsCiAgICAiUHJvY2Vzc1RpbWVvdXQiOiAzMDAsCiAgICAiQ29tbWFuZERlbGF5IjogMAogIH0KRU9GCgplY2hvICJlbmFibGluZyBhcm8gc2VydmljZXMiCmZvciBzZXJ2aWNlIGluIGFyby1kYnRva2VuIGFyby1tb25pdG9yIGFyby1wb3J0YWwgYXJvLXJwIGF1b21zIGF6c2VjZCBhenNlY21vbmQgbWRzZCBtZG0gY2hyb255ZCBmbHVlbnRiaXQ7IGRvCiAgc3lzdGVtY3RsIGVuYWJsZSAkc2VydmljZS5zZXJ2aWNlCmRvbmUKCmZvciBzY2FuIGluIGJhc2VsaW5lIGNsYW1hdiBzb2Z0d2FyZTsgZG8KICAvdXNyL2xvY2FsL2Jpbi9henNlY2QgY29uZmlnIC1zICRzY2FuIC1kIFAxRApkb25lCgplY2hvICJyZWJvb3RpbmciCnJlc3RvcmVjb24gLVJGIC92YXIvbG9nLyoKKHNsZWVwIDMwOyByZWJvb3QpICYK')))]" + "script": "[base64(concat(base64ToString('c2V0IC1leAoK'),'ACRRESOURCEID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('acrResourceId')),''')\n','ADMINAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('adminApiClientCertCommonName')),''')\n','ARMAPICLIENTCERTCOMMONNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armApiClientCertCommonName')),''')\n','ARMCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('armClientId')),''')\n','AZURECLOUDNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureCloudName')),''')\n','AZURESECPACKQUALYSURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackQualysUrl')),''')\n','AZURESECPACKVSATENANTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('azureSecPackVSATenantId')),''')\n','CLUSTERMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdmAccount')),''')\n','CLUSTERMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdAccount')),''')\n','CLUSTERMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdConfigVersion')),''')\n','CLUSTERMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterMdsdNamespace')),''')\n','CLUSTERPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterParentDomainName')),''')\n','DATABASEACCOUNTNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('databaseAccountName')),''')\n','FLUENTBITIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fluentbitImage')),''')\n','FPCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpClientId')),''')\n','FPSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('fpServicePrincipalId')),''')\n','GATEWAYDOMAINS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayDomains')),''')\n','GATEWAYRESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayResourceGroupName')),''')\n','GATEWAYSERVICEPRINCIPALID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('gatewayServicePrincipalId')),''')\n','KEYVAULTDNSSUFFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultDNSSuffix')),''')\n','KEYVAULTPREFIX=$(base64 -d \u003c\u003c\u003c''',base64(parameters('keyvaultPrefix')),''')\n','MDMFRONTENDURL=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdmFrontendUrl')),''')\n','MDSDENVIRONMENT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('mdsdEnvironment')),''')\n','PORTALACCESSGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalAccessGroupIds')),''')\n','PORTALCLIENTID=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalClientId')),''')\n','PORTALELEVATEDGROUPIDS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('portalElevatedGroupIds')),''')\n','RPFEATURES=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpFeatures')),''')\n','RPIMAGE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpImage')),''')\n','RPMDMACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdmAccount')),''')\n','RPMDSDACCOUNT=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdAccount')),''')\n','RPMDSDCONFIGVERSION=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdConfigVersion')),''')\n','RPMDSDNAMESPACE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpMdsdNamespace')),''')\n','RPPARENTDOMAINNAME=$(base64 -d \u003c\u003c\u003c''',base64(parameters('rpParentDomainName')),''')\n','CLUSTERSINSTALLVIAHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersInstallViaHive')),''')\n','CLUSTERSADOPTBYHIVE=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clustersAdoptByHive')),''')\n','CLUSTERDEFAULTINSTALLERPULLSPEC=$(base64 -d \u003c\u003c\u003c''',base64(parameters('clusterDefaultInstallerPullspec')),''')\n','USECHECKACCESS=$(base64 -d \u003c\u003c\u003c''',base64(parameters('useCheckAccess')),''')\n','ADMINAPICABUNDLE=''',parameters('adminApiCaBundle'),'''\n','ARMAPICABUNDLE=''',parameters('armApiCaBundle'),'''\n','MDMIMAGE=''/distroless/genevamdm:2.2024.517.533-b73893-20240522t0954@sha256:939df9d7b6660874697f8ebed1fe56504f86d92f99801a9dc6fd98e9176d3f75''\n','LOCATION=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().location),''')\n','SUBSCRIPTIONID=$(base64 -d \u003c\u003c\u003c''',base64(subscription().subscriptionId),''')\n','RESOURCEGROUPNAME=$(base64 -d \u003c\u003c\u003c''',base64(resourceGroup().name),''')\n','\n',base64ToString('IyEvYmluL2Jhc2gKCmVjaG8gInNldHRpbmcgc3NoIHBhc3N3b3JkIGF1dGhlbnRpY2F0aW9uIgojIFdlIG5lZWQgdG8gbWFudWFsbHkgc2V0IFBhc3N3b3JkQXV0aGVudGljYXRpb24gdG8gdHJ1ZSBpbiBvcmRlciBmb3IgdGhlIFZNU1MgQWNjZXNzIEpJVCB0byB3b3JrCnNlZCAtaSAncy9QYXNzd29yZEF1dGhlbnRpY2F0aW9uIG5vL1Bhc3N3b3JkQXV0aGVudGljYXRpb24geWVzL2cnIC9ldGMvc3NoL3NzaGRfY29uZmlnCnN5c3RlbWN0bCByZWxvYWQgc3NoZC5zZXJ2aWNlCgojQWRkaW5nIHJldHJ5IGxvZ2ljIHRvIHl1bSBjb21tYW5kcyBpbiBvcmRlciB0byBhdm9pZCBzdGFsbGluZyBvdXQgb24gcmVzb3VyY2UgbG9ja3MKZWNobyAicnVubmluZyBSSFVJIGZpeCIKZm9yIGF0dGVtcHQgaW4gezEuLjYwfTsgZG8KICB5dW0gdXBkYXRlIC15IC0tZGlzYWJsZXJlcG89JyonIC0tZW5hYmxlcmVwbz0ncmh1aS1taWNyb3NvZnQtYXp1cmUqJyAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAicnVubmluZyB5dW0gdXBkYXRlIgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwogIHl1bSAteSAteCBXQUxpbnV4QWdlbnQgLXggV0FMaW51eEFnZW50LXVkZXYgdXBkYXRlIC0tYWxsb3dlcmFzaW5nICYmIGJyZWFrCiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgplY2hvICJleHRlbmRpbmcgcGFydGl0aW9uIHRhYmxlIgojIExpbnV4IGJsb2NrIGRldmljZXMgYXJlIGluY29uc2lzdGVudGx5IG5hbWVkCiMgaXQncyBkaWZmaWN1bHQgdG8gdGllIHRoZSBsdm0gcHYgdG8gdGhlIHBoeXNpY2FsIGRpc2sgdXNpbmcgL2Rldi9kaXNrIGZpbGVzLCB3aGljaCBpcyB3aHkgbHZzIGlzIHVzZWQgaGVyZQpwaHlzaWNhbERpc2s9IiQobHZzIC1vIGRldmljZXMgLWEgfCBoZWFkIC1uMiB8IHRhaWwgLW4xIHwgY3V0IC1kICcgJyAtZiAzIHwgY3V0IC1kIFwoIC1mIDEgfCB0ciAtZCAnWzpkaWdpdDpdJykiCmdyb3dwYXJ0ICIkcGh5c2ljYWxEaXNrIiAyCgplY2hvICJleHRlbmRpbmcgZmlsZXN5c3RlbXMiCmx2ZXh0ZW5kIC1sICsyMCVGUkVFIC9kZXYvcm9vdHZnL3Jvb3Rsdgp4ZnNfZ3Jvd2ZzIC8KCmx2ZXh0ZW5kIC1sICsxMDAlRlJFRSAvZGV2L3Jvb3R2Zy92YXJsdgp4ZnNfZ3Jvd2ZzIC92YXIKCmVjaG8gImltcG9ydGluZyBycG0gcmVwb3NpdG9yaWVzIgpycG0gLS1pbXBvcnQgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9SUE0tR1BHLUtFWS1FUEVMLTgKcnBtIC0taW1wb3J0IGh0dHBzOi8vcGFja2FnZXMubWljcm9zb2Z0LmNvbS9rZXlzL21pY3Jvc29mdC5hc2MKCmZvciBhdHRlbXB0IGluIHsxLi42MH07IGRvCiAgeXVtIC15IGluc3RhbGwgaHR0cHM6Ly9kbC5mZWRvcmFwcm9qZWN0Lm9yZy9wdWIvZXBlbC9lcGVsLXJlbGVhc2UtbGF0ZXN0LTgubm9hcmNoLnJwbSAmJiBicmVhawogIGlmIFtbICR7YXR0ZW1wdH0gLWx0IDYwIF1dOyB0aGVuIHNsZWVwIDMwOyBlbHNlIGV4aXQgMTsgZmkKZG9uZQoKZWNobyAiY29uZmlndXJpbmcgbG9ncm90YXRlIgpjYXQgPi9ldGMvbG9ncm90YXRlLmNvbmYgPDwnRU9GJwojIHNlZSAibWFuIGxvZ3JvdGF0ZSIgZm9yIGRldGFpbHMKIyByb3RhdGUgbG9nIGZpbGVzIHdlZWtseQp3ZWVrbHkKCiMga2VlcCAyIHdlZWtzIHdvcnRoIG9mIGJhY2tsb2dzCnJvdGF0ZSAyCgojIGNyZWF0ZSBuZXcgKGVtcHR5KSBsb2cgZmlsZXMgYWZ0ZXIgcm90YXRpbmcgb2xkIG9uZXMKY3JlYXRlCgojIHVzZSBkYXRlIGFzIGEgc3VmZml4IG9mIHRoZSByb3RhdGVkIGZpbGUKZGF0ZWV4dAoKIyB1bmNvbW1lbnQgdGhpcyBpZiB5b3Ugd2FudCB5b3VyIGxvZyBmaWxlcyBjb21wcmVzc2VkCmNvbXByZXNzCgojIFJQTSBwYWNrYWdlcyBkcm9wIGxvZyByb3RhdGlvbiBpbmZvcm1hdGlvbiBpbnRvIHRoaXMgZGlyZWN0b3J5CmluY2x1ZGUgL2V0Yy9sb2dyb3RhdGUuZAoKIyBubyBwYWNrYWdlcyBvd24gd3RtcCBhbmQgYnRtcCAtLSB3ZSdsbCByb3RhdGUgdGhlbSBoZXJlCi92YXIvbG9nL3d0bXAgewogICAgbW9udGhseQogICAgY3JlYXRlIDA2NjQgcm9vdCB1dG1wCiAgICAgICAgbWluc2l6ZSAxTQogICAgcm90YXRlIDEKfQoKL3Zhci9sb2cvYnRtcCB7CiAgICBtaXNzaW5nb2sKICAgIG1vbnRobHkKICAgIGNyZWF0ZSAwNjAwIHJvb3QgdXRtcAogICAgcm90YXRlIDEKfQpFT0YKCmVjaG8gImNvbmZpZ3VyaW5nIHl1bSByZXBvc2l0b3J5IGFuZCBydW5uaW5nIHl1bSB1cGRhdGUiCmNhdCA+L2V0Yy95dW0ucmVwb3MuZC9henVyZS5yZXBvIDw8J0VPRicKW2F6dXJlLWNsaV0KbmFtZT1henVyZS1jbGkKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmUtY2xpCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPXllcwoKW2F6dXJlY29yZV0KbmFtZT1henVyZWNvcmUKYmFzZXVybD1odHRwczovL3BhY2thZ2VzLm1pY3Jvc29mdC5jb20veXVtcmVwb3MvYXp1cmVjb3JlCmVuYWJsZWQ9eWVzCmdwZ2NoZWNrPW5vCkVPRgoKc2VtYW5hZ2UgZmNvbnRleHQgLWEgLXQgdmFyX2xvZ190ICIvdmFyL2xvZy9qb3VybmFsKC8uKik/Igpta2RpciAtcCAvdmFyL2xvZy9qb3VybmFsCgpmb3IgYXR0ZW1wdCBpbiB7MS4uNjB9OyBkbwp5dW0gLXkgaW5zdGFsbCBjbGFtYXYgYXpzZWMtY2xhbWF2IGF6c2VjLW1vbml0b3IgYXp1cmUtY2xpIGF6dXJlLW1kc2QgYXp1cmUtc2VjdXJpdHkgcG9kbWFuIHBvZG1hbi1kb2NrZXIgb3BlbnNzbC1wZXJsIHB5dGhvbjMgJiYgYnJlYWsKICAjIGhhY2sgLSB3ZSBhcmUgaW5zdGFsbGluZyBweXRob24zIG9uIGhvc3RzIGR1ZSB0byBhbiBpc3N1ZSB3aXRoIEF6dXJlIExpbnV4IEV4dGVuc2lvbnMgaHR0cHM6Ly9naXRodWIuY29tL0F6dXJlL2F6dXJlLWxpbnV4LWV4dGVuc2lvbnMvcHVsbC8xNTA1CiAgaWYgW1sgJHthdHRlbXB0fSAtbHQgNjAgXV07IHRoZW4gc2xlZXAgMzA7IGVsc2UgZXhpdCAxOyBmaQpkb25lCgojIGh0dHBzOi8vYWNjZXNzLnJlZGhhdC5jb20vc2VjdXJpdHkvY3ZlL2N2ZS0yMDIwLTEzNDAxCmVjaG8gImFwcGx5aW5nIGZpcmV3YWxsIHJ1bGVzIgpjYXQgPi9ldGMvc3lzY3RsLmQvMDItZGlzYWJsZS1hY2NlcHQtcmEuY29uZiA8PCdFT0YnCm5ldC5pcHY2LmNvbmYuYWxsLmFjY2VwdF9yYT0wCkVPRgoKY2F0ID4vZXRjL3N5c2N0bC5kLzAxLWRpc2FibGUtY29yZS5jb25mIDw8J0VPRicKa2VybmVsLmNvcmVfcGF0dGVybiA9IHwvYmluL3RydWUKRU9GCnN5c2N0bCAtLXN5c3RlbQoKZmlyZXdhbGwtY21kIC0tYWRkLXBvcnQ9NDQzL3RjcCAtLXBlcm1hbmVudApmaXJld2FsbC1jbWQgLS1hZGQtcG9ydD00NDQvdGNwIC0tcGVybWFuZW50CmZpcmV3YWxsLWNtZCAtLWFkZC1wb3J0PTIyMjIvdGNwIC0tcGVybWFuZW50CgpleHBvcnQgQVpVUkVfQ0xPVURfTkFNRT0kQVpVUkVDTE9VRE5BTUUKCmVjaG8gImxvZ2dpbmcgaW50byBwcm9kIGFjciIKYXogbG9naW4gLWkgLS1hbGxvdy1uby1zdWJzY3JpcHRpb25zCgojIFN1cHByZXNzIGVtdWxhdGlvbiBvdXRwdXQgZm9yIHBvZG1hbiBpbnN0ZWFkIG9mIGRvY2tlciBmb3IgYXogYWNyIGNvbXBhdGFiaWxpdHkKbWtkaXIgLXAgL2V0Yy9jb250YWluZXJzLwp0b3VjaCAvZXRjL2NvbnRhaW5lcnMvbm9kb2NrZXIKCm1rZGlyIC1wIC9yb290Ly5kb2NrZXIKUkVHSVNUUllfQVVUSF9GSUxFPS9yb290Ly5kb2NrZXIvY29uZmlnLmpzb24gYXogYWNyIGxvZ2luIC0tbmFtZSAiJChzZWQgLWUgJ3N8LiovfHwnIDw8PCIkQUNSUkVTT1VSQ0VJRCIpIgoKTURNSU1BR0U9IiR7UlBJTUFHRSUlLyp9LyR7TURNSU1BR0UjKi99Igpkb2NrZXIgcHVsbCAiJE1ETUlNQUdFIgpkb2NrZXIgcHVsbCAiJFJQSU1BR0UiCmRvY2tlciBwdWxsICIkRkxVRU5UQklUSU1BR0UiCgpheiBsb2dvdXQKCmVjaG8gImNvbmZpZ3VyaW5nIGZsdWVudGJpdCBzZXJ2aWNlIgpta2RpciAtcCAvZXRjL2ZsdWVudGJpdC8KbWtkaXIgLXAgL3Zhci9saWIvZmx1ZW50CgpjYXQgPi9ldGMvZmx1ZW50Yml0L2ZsdWVudGJpdC5jb25mIDw8J0VPRicKW0lOUFVUXQoJTmFtZSBzeXN0ZW1kCglUYWcgam91cm5hbGQKCVN5c3RlbWRfRmlsdGVyIF9DT01NPWFybwoJREIgL3Zhci9saWIvZmx1ZW50L2pvdXJuYWxkYgoKW0ZJTFRFUl0KCU5hbWUgbW9kaWZ5CglNYXRjaCBqb3VybmFsZAoJUmVtb3ZlX3dpbGRjYXJkIF8KCVJlbW92ZSBUSU1FU1RBTVAKCltGSUxURVJdCglOYW1lIHJld3JpdGVfdGFnCglNYXRjaCBqb3VybmFsZAoJUnVsZSAkTE9HS0lORCBhc3luY3FvcyBhc3luY3FvcyB0cnVlCgpbRklMVEVSXQoJTmFtZSBtb2RpZnkKCU1hdGNoIGFzeW5jcW9zCglSZW1vdmUgQ0xJRU5UX1BSSU5DSVBBTF9OQU1FCglSZW1vdmUgRklMRQoJUmVtb3ZlIENPTVBPTkVOVAoKW0ZJTFRFUl0KCU5hbWUgcmV3cml0ZV90YWcKCU1hdGNoIGpvdXJuYWxkCglSdWxlICRMT0dLSU5EIGlmeGF1ZGl0IGlmeGF1ZGl0IGZhbHNlCgpbT1VUUFVUXQoJTmFtZSBmb3J3YXJkCglNYXRjaCAqCglQb3J0IDI5MjMwCkVPRgoKZWNobyAiRkxVRU5UQklUSU1BR0U9JEZMVUVOVEJJVElNQUdFIiA+L2V0Yy9zeXNjb25maWcvZmx1ZW50Yml0CgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZmx1ZW50Yml0LnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldApTdGFydExpbWl0SW50ZXJ2YWxTZWM9MAoKW1NlcnZpY2VdClJlc3RhcnRTZWM9MXMKRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2ZsdWVudGJpdApFeGVjU3RhcnRQcmU9LS91c3IvYmluL2RvY2tlciBybSAtZiAlTgpFeGVjU3RhcnQ9L3Vzci9iaW4vZG9ja2VyIHJ1biBcCiAgLS1zZWN1cml0eS1vcHQgbGFiZWw9ZGlzYWJsZSBcCiAgLS1lbnRyeXBvaW50IC9vcHQvdGQtYWdlbnQtYml0L2Jpbi90ZC1hZ2VudC1iaXQgXAogIC0tbmV0PWhvc3QgXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtdiAvZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZjovZXRjL2ZsdWVudGJpdC9mbHVlbnRiaXQuY29uZiBcCiAgLXYgL3Zhci9saWIvZmx1ZW50Oi92YXIvbGliL2ZsdWVudDp6IFwKICAtdiAvdmFyL2xvZy9qb3VybmFsOi92YXIvbG9nL2pvdXJuYWw6cm8gXAogIC12IC9ldGMvbWFjaGluZS1pZDovZXRjL21hY2hpbmUtaWQ6cm8gXAogICRGTFVFTlRCSVRJTUFHRSBcCiAgLWMgL2V0Yy9mbHVlbnRiaXQvZmx1ZW50Yml0LmNvbmYKCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wICVOClJlc3RhcnQ9YWx3YXlzClJlc3RhcnRTZWM9NQpTdGFydExpbWl0SW50ZXJ2YWw9MAoKW0luc3RhbGxdCldhbnRlZEJ5PW11bHRpLXVzZXIudGFyZ2V0CkVPRgoKbWtkaXIgL2V0Yy9hcm8tcnAKYmFzZTY0IC1kIDw8PCIkQURNSU5BUElDQUJVTkRMRSIgPi9ldGMvYXJvLXJwL2FkbWluLWNhLWJ1bmRsZS5wZW0KaWYgW1sgLW4gIiRBUk1BUElDQUJVTkRMRSIgXV07IHRoZW4KICBiYXNlNjQgLWQgPDw8IiRBUk1BUElDQUJVTkRMRSIgPi9ldGMvYXJvLXJwL2FybS1jYS1idW5kbGUucGVtCmZpCmNob3duIC1SIDEwMDA6MTAwMCAvZXRjL2Fyby1ycAoKZWNobyAiY29uZmlndXJpbmcgbWRtIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvbWRtIDw8RU9GCk1ETUZST05URU5EVVJMPSckTURNRlJPTlRFTkRVUkwnCk1ETUlNQUdFPSckTURNSU1BR0UnCk1ETVNPVVJDRUVOVklST05NRU5UPSckTE9DQVRJT04nCk1ETVNPVVJDRVJPTEU9cnAKTURNU09VUkNFUk9MRUlOU1RBTkNFPSckKGhvc3RuYW1lKScKRU9GCgpta2RpciAvdmFyL2V0dwpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vbWRtLnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldAoKW1NlcnZpY2VdCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9tZG0KRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0tZW50cnlwb2ludCAvdXNyL3NiaW4vTWV0cmljc0V4dGVuc2lvbiBcCiAgLS1ob3N0bmFtZSAlSCBcCiAgLS1uYW1lICVOIFwKICAtLXJtIFwKICAtLWNhcC1kcm9wIG5ldF9yYXcgXAogIC1tIDJnIFwKICAtdiAvZXRjL21kbS5wZW06L2V0Yy9tZG0ucGVtIFwKICAtdiAvdmFyL2V0dzovdmFyL2V0dzp6IFwKICAkTURNSU1BR0UgXAogIC1DZXJ0RmlsZSAvZXRjL21kbS5wZW0gXAogIC1Gcm9udEVuZFVybCAkTURNRlJPTlRFTkRVUkwgXAogIC1Mb2dnZXIgQ29uc29sZSBcCiAgLUxvZ0xldmVsIFdhcm5pbmcgXAogIC1Qcml2YXRlS2V5RmlsZSAvZXRjL21kbS5wZW0gXAogIC1Tb3VyY2VFbnZpcm9ubWVudCAkTURNU09VUkNFRU5WSVJPTk1FTlQgXAogIC1Tb3VyY2VSb2xlICRNRE1TT1VSQ0VST0xFIFwKICAtU291cmNlUm9sZUluc3RhbmNlICRNRE1TT1VSQ0VST0xFSU5TVEFOQ0UKRXhlY1N0b3A9L3Vzci9iaW4vZG9ja2VyIHN0b3AgJU4KUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgplY2hvICJjb25maWd1cmluZyBhcm8tcnAgc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9hcm8tcnAgPDxFT0YKQUNSX1JFU09VUkNFX0lEPSckQUNSUkVTT1VSQ0VJRCcKQURNSU5fQVBJX0NMSUVOVF9DRVJUX0NPTU1PTl9OQU1FPSckQURNSU5BUElDTElFTlRDRVJUQ09NTU9OTkFNRScKQVJNX0FQSV9DTElFTlRfQ0VSVF9DT01NT05fTkFNRT0nJEFSTUFQSUNMSUVOVENFUlRDT01NT05OQU1FJwpBWlVSRV9BUk1fQ0xJRU5UX0lEPSckQVJNQ0xJRU5USUQnCkFaVVJFX0ZQX0NMSUVOVF9JRD0nJEZQQ0xJRU5USUQnCkFaVVJFX0ZQX1NFUlZJQ0VfUFJJTkNJUEFMX0lEPSckRlBTRVJWSUNFUFJJTkNJUEFMSUQnCkNMVVNURVJfTURNX0FDQ09VTlQ9JyRDTFVTVEVSTURNQUNDT1VOVCcKQ0xVU1RFUl9NRE1fTkFNRVNQQUNFPVJQCkNMVVNURVJfTURTRF9BQ0NPVU5UPSckQ0xVU1RFUk1EU0RBQ0NPVU5UJwpDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT049JyRDTFVTVEVSTURTRENPTkZJR1ZFUlNJT04nCkNMVVNURVJfTURTRF9OQU1FU1BBQ0U9JyRDTFVTVEVSTURTRE5BTUVTUEFDRScKREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKRE9NQUlOX05BTUU9JyRMT0NBVElPTi4kQ0xVU1RFUlBBUkVOVERPTUFJTk5BTUUnCkdBVEVXQVlfRE9NQUlOUz0nJEdBVEVXQVlET01BSU5TJwpHQVRFV0FZX1JFU09VUkNFR1JPVVA9JyRHQVRFV0FZUkVTT1VSQ0VHUk9VUE5BTUUnCktFWVZBVUxUX1BSRUZJWD0nJEtFWVZBVUxUUFJFRklYJwpNRE1fQUNDT1VOVD0nJFJQTURNQUNDT1VOVCcKTURNX05BTUVTUEFDRT1SUApNRFNEX0VOVklST05NRU5UPSckTURTREVOVklST05NRU5UJwpSUF9GRUFUVVJFUz0nJFJQRkVBVFVSRVMnClJQSU1BR0U9JyRSUElNQUdFJwpBUk9fSU5TVEFMTF9WSUFfSElWRT0nJENMVVNURVJTSU5TVEFMTFZJQUhJVkUnCkFST19ISVZFX0RFRkFVTFRfSU5TVEFMTEVSX1BVTExTUEVDPSckQ0xVU1RFUkRFRkFVTFRJTlNUQUxMRVJQVUxMU1BFQycKQVJPX0FET1BUX0JZX0hJVkU9JyRDTFVTVEVSU0FET1BUQllISVZFJwpVU0VfQ0hFQ0tBQ0NFU1M9JyRVU0VDSEVDS0FDQ0VTUycKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vYXJvLXJwLnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldAoKW1NlcnZpY2VdCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9hcm8tcnAKRXhlY1N0YXJ0UHJlPS0vdXNyL2Jpbi9kb2NrZXIgcm0gLWYgJU4KRXhlY1N0YXJ0PS91c3IvYmluL2RvY2tlciBydW4gXAogIC0taG9zdG5hbWUgJUggXAogIC0tbmFtZSAlTiBcCiAgLS1ybSBcCiAgLS1jYXAtZHJvcCBuZXRfcmF3IFwKICAtZSBBQ1JfUkVTT1VSQ0VfSUQgXAogIC1lIEFETUlOX0FQSV9DTElFTlRfQ0VSVF9DT01NT05fTkFNRSBcCiAgLWUgQVJNX0FQSV9DTElFTlRfQ0VSVF9DT01NT05fTkFNRSBcCiAgLWUgQVpVUkVfQVJNX0NMSUVOVF9JRCBcCiAgLWUgQVpVUkVfRlBfQ0xJRU5UX0lEIFwKICAtZSBDTFVTVEVSX01ETV9BQ0NPVU5UIFwKICAtZSBDTFVTVEVSX01ETV9OQU1FU1BBQ0UgXAogIC1lIENMVVNURVJfTURTRF9BQ0NPVU5UIFwKICAtZSBDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT04gXAogIC1lIENMVVNURVJfTURTRF9OQU1FU1BBQ0UgXAogIC1lIERBVEFCQVNFX0FDQ09VTlRfTkFNRSBcCiAgLWUgRE9NQUlOX05BTUUgXAogIC1lIEdBVEVXQVlfRE9NQUlOUyBcCiAgLWUgR0FURVdBWV9SRVNPVVJDRUdST1VQIFwKICAtZSBLRVlWQVVMVF9QUkVGSVggXAogIC1lIE1ETV9BQ0NPVU5UIFwKICAtZSBNRE1fTkFNRVNQQUNFIFwKICAtZSBNRFNEX0VOVklST05NRU5UIFwKICAtZSBSUF9GRUFUVVJFUyBcCiAgLWUgQVJPX0lOU1RBTExfVklBX0hJVkUgXAogIC1lIEFST19ISVZFX0RFRkFVTFRfSU5TVEFMTEVSX1BVTExTUEVDIFwKICAtZSBBUk9fQURPUFRfQllfSElWRSBcCiAgLWUgVVNFX0NIRUNLQUNDRVNTIFwKICAtbSAyZyBcCiAgLXAgNDQzOjg0NDMgXAogIC12IC9ldGMvYXJvLXJwOi9ldGMvYXJvLXJwIFwKICAtdiAvcnVuL3N5c3RlbWQvam91cm5hbDovcnVuL3N5c3RlbWQvam91cm5hbCBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgJFJQSU1BR0UgXAogIHJwCkV4ZWNTdG9wPS91c3IvYmluL2RvY2tlciBzdG9wIC10IDM2MDAgJU4KVGltZW91dFN0b3BTZWM9MzYwMApSZXN0YXJ0PWFsd2F5cwpSZXN0YXJ0U2VjPTEKU3RhcnRMaW1pdEludGVydmFsPTAKCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCiMgRE9NQUlOX05BTUUsIENMVVNURVJfTURTRF9BQ0NPVU5ULCBDTFVTVEVSX01EU0RfQ09ORklHX1ZFUlNJT04sIEdBVEVXQVlfRE9NQUlOUywgR0FURVdBWV9SRVNPVVJDRUdST1VQLCBNRFNEX0VOVklST05NRU5UIENMVVNURVJfTURTRF9OQU1FU1BBQ0UKIyBhcmUgbm90IHVzZWQsIGJ1dCBjYW4ndCBlYXNpbHkgYmUgcmVmYWN0b3JlZCBvdXQuIFNob3VsZCBiZSByZXZpc2l0ZWQgaW4gdGhlIGZ1dHVyZS4KZWNobyAiY29uZmlndXJpbmcgYXJvLW1vbml0b3Igc2VydmljZSIKY2F0ID4vZXRjL3N5c2NvbmZpZy9hcm8tbW9uaXRvciA8PEVPRgpBWlVSRV9GUF9DTElFTlRfSUQ9JyRGUENMSUVOVElEJwpET01BSU5fTkFNRT0nJExPQ0FUSU9OLiRDTFVTVEVSUEFSRU5URE9NQUlOTkFNRScKQ0xVU1RFUl9NRFNEX0FDQ09VTlQ9JyRDTFVTVEVSTURTREFDQ09VTlQnCkNMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTj0nJENMVVNURVJNRFNEQ09ORklHVkVSU0lPTicKR0FURVdBWV9ET01BSU5TPSckR0FURVdBWURPTUFJTlMnCkdBVEVXQVlfUkVTT1VSQ0VHUk9VUD0nJEdBVEVXQVlSRVNPVVJDRUdST1VQTkFNRScKTURTRF9FTlZJUk9OTUVOVD0nJE1EU0RFTlZJUk9OTUVOVCcKQ0xVU1RFUl9NRFNEX05BTUVTUEFDRT0nJENMVVNURVJNRFNETkFNRVNQQUNFJwpDTFVTVEVSX01ETV9BQ0NPVU5UPSckQ0xVU1RFUk1ETUFDQ09VTlQnCkNMVVNURVJfTURNX05BTUVTUEFDRT1CQk0KREFUQUJBU0VfQUNDT1VOVF9OQU1FPSckREFUQUJBU0VBQ0NPVU5UTkFNRScKS0VZVkFVTFRfUFJFRklYPSckS0VZVkFVTFRQUkVGSVgnCk1ETV9BQ0NPVU5UPSckUlBNRE1BQ0NPVU5UJwpNRE1fTkFNRVNQQUNFPUJCTQpSUElNQUdFPSckUlBJTUFHRScKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vYXJvLW1vbml0b3Iuc2VydmljZSA8PCdFT0YnCltVbml0XQpBZnRlcj1uZXR3b3JrLW9ubGluZS50YXJnZXQKV2FudHM9bmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KRW52aXJvbm1lbnRGaWxlPS9ldGMvc3lzY29uZmlnL2Fyby1tb25pdG9yCkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQVpVUkVfRlBfQ0xJRU5UX0lEIFwKICAtZSBET01BSU5fTkFNRSBcCiAgLWUgQ0xVU1RFUl9NRFNEX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURTRF9DT05GSUdfVkVSU0lPTiBcCiAgLWUgR0FURVdBWV9ET01BSU5TIFwKICAtZSBHQVRFV0FZX1JFU09VUkNFR1JPVVAgXAogIC1lIE1EU0RfRU5WSVJPTk1FTlQgXAogIC1lIENMVVNURVJfTURTRF9OQU1FU1BBQ0UgXAogIC1lIENMVVNURVJfTURNX0FDQ09VTlQgXAogIC1lIENMVVNURVJfTURNX05BTUVTUEFDRSBcCiAgLWUgREFUQUJBU0VfQUNDT1VOVF9OQU1FIFwKICAtZSBLRVlWQVVMVF9QUkVGSVggXAogIC1lIE1ETV9BQ0NPVU5UIFwKICAtZSBNRE1fTkFNRVNQQUNFIFwKICAtbSAyLjVnIFwKICAtdiAvcnVuL3N5c3RlbWQvam91cm5hbDovcnVuL3N5c3RlbWQvam91cm5hbCBcCiAgLXYgL3Zhci9ldHc6L3Zhci9ldHc6eiBcCiAgJFJQSU1BR0UgXAogIG1vbml0b3IKUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xClN0YXJ0TGltaXRJbnRlcnZhbD0wCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgplY2hvICJjb25maWd1cmluZyBhcm8tcG9ydGFsIHNlcnZpY2UiCmNhdCA+L2V0Yy9zeXNjb25maWcvYXJvLXBvcnRhbCA8PEVPRgpBWlVSRV9QT1JUQUxfQUNDRVNTX0dST1VQX0lEUz0nJFBPUlRBTEFDQ0VTU0dST1VQSURTJwpBWlVSRV9QT1JUQUxfQ0xJRU5UX0lEPSckUE9SVEFMQ0xJRU5USUQnCkFaVVJFX1BPUlRBTF9FTEVWQVRFRF9HUk9VUF9JRFM9JyRQT1JUQUxFTEVWQVRFREdST1VQSURTJwpEQVRBQkFTRV9BQ0NPVU5UX05BTUU9JyREQVRBQkFTRUFDQ09VTlROQU1FJwpLRVlWQVVMVF9QUkVGSVg9JyRLRVlWQVVMVFBSRUZJWCcKTURNX0FDQ09VTlQ9JyRSUE1ETUFDQ09VTlQnCk1ETV9OQU1FU1BBQ0U9UG9ydGFsClBPUlRBTF9IT1NUTkFNRT0nJExPQ0FUSU9OLmFkbWluLiRSUFBBUkVOVERPTUFJTk5BTUUnClJQSU1BR0U9JyRSUElNQUdFJwpFT0YKCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9hcm8tcG9ydGFsLnNlcnZpY2UgPDwnRU9GJwpbVW5pdF0KQWZ0ZXI9bmV0d29yay1vbmxpbmUudGFyZ2V0CldhbnRzPW5ldHdvcmstb25saW5lLnRhcmdldApTdGFydExpbWl0SW50ZXJ2YWw9MAoKW1NlcnZpY2VdCkVudmlyb25tZW50RmlsZT0vZXRjL3N5c2NvbmZpZy9hcm8tcG9ydGFsCkV4ZWNTdGFydFByZT0tL3Vzci9iaW4vZG9ja2VyIHJtIC1mICVOCkV4ZWNTdGFydD0vdXNyL2Jpbi9kb2NrZXIgcnVuIFwKICAtLWhvc3RuYW1lICVIIFwKICAtLW5hbWUgJU4gXAogIC0tcm0gXAogIC0tY2FwLWRyb3AgbmV0X3JhdyBcCiAgLWUgQVpVUkVfUE9SVEFMX0FDQ0VTU19HUk9VUF9JRFMgXAogIC1lIEFaVVJFX1BPUlRBTF9DTElFTlRfSUQgXAogIC1lIEFaVVJFX1BPUlRBTF9FTEVWQVRFRF9HUk9VUF9JRFMgXAogIC1lIERBVEFCQVNFX0FDQ09VTlRfTkFNRSBcCiAgLWUgS0VZVkFVTFRfUFJFRklYIFwKICAtZSBNRE1fQUNDT1VOVCBcCiAgLWUgTURNX05BTUVTUEFDRSBcCiAgLWUgUE9SVEFMX0hPU1ROQU1FIFwKICAtbSAyZyBcCiAgLXAgNDQ0Ojg0NDQgXAogIC1wIDIyMjI6MjIyMiBcCiAgLXYgL3J1bi9zeXN0ZW1kL2pvdXJuYWw6L3J1bi9zeXN0ZW1kL2pvdXJuYWwgXAogIC12IC92YXIvZXR3Oi92YXIvZXR3OnogXAogICRSUElNQUdFIFwKICBwb3J0YWwKUmVzdGFydD1hbHdheXMKUmVzdGFydFNlYz0xCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgplY2hvICJjb25maWd1cmluZyBtZHNkIGFuZCBtZG0gc2VydmljZXMiCmNoY29uIC1SIHN5c3RlbV91Om9iamVjdF9yOnZhcl9sb2dfdDpzMCAvdmFyL29wdC9taWNyb3NvZnQvbGludXhtb25hZ2VudAoKbWtkaXIgLXAgL3Zhci9saWIvd2FhZ2VudC9NaWNyb3NvZnQuQXp1cmUuS2V5VmF1bHQuU3RvcmUKCmZvciB2YXIgaW4gIm1kc2QiICJtZG0iOyBkbwpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZG93bmxvYWQtJHZhci1jcmVkZW50aWFscy5zZXJ2aWNlIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1QZXJpb2RpYyAkdmFyIGNyZWRlbnRpYWxzIHJlZnJlc2gKCltTZXJ2aWNlXQpUeXBlPW9uZXNob3QKRXhlY1N0YXJ0PS91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoICR2YXIKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vZG93bmxvYWQtJHZhci1jcmVkZW50aWFscy50aW1lciA8PEVPRgpbVW5pdF0KRGVzY3JpcHRpb249UGVyaW9kaWMgJHZhciBjcmVkZW50aWFscyByZWZyZXNoCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApXYW50cz1uZXR3b3JrLW9ubGluZS50YXJnZXQKCltUaW1lcl0KT25Cb290U2VjPTBtaW4KT25DYWxlbmRhcj0wLzEyOjAwOjAwCkFjY3VyYWN5U2VjPTVzCgpbSW5zdGFsbF0KV2FudGVkQnk9dGltZXJzLnRhcmdldApFT0YKZG9uZQoKY2F0ID4vdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCA8PEVPRgojIS9iaW4vYmFzaApzZXQgLWV1CgpDT01QT05FTlQ9IlwkMSIKZWNobyAiRG93bmxvYWQgXCRDT01QT05FTlQgY3JlZGVudGlhbHMiCgpURU1QX0RJUj1cJChta3RlbXAgLWQpCmV4cG9ydCBBWlVSRV9DT05GSUdfRElSPVwkKG1rdGVtcCAtZCkKCmVjaG8gIkxvZ2dpbmcgaW50byBBenVyZS4uLiIKUkVUUklFUz0zCndoaWxlIFsgIlwkUkVUUklFUyIgLWd0IDAgXTsgZG8KICAgIGlmIGF6IGxvZ2luIC1pIC0tYWxsb3ctbm8tc3Vic2NyaXB0aW9ucwogICAgdGhlbgogICAgICAgIGVjaG8gImF6IGxvZ2luIHN1Y2Nlc3NmdWwiCiAgICAgICAgYnJlYWsKICAgIGVsc2UKICAgICAgICBlY2hvICJheiBsb2dpbiBmYWlsZWQuIFJldHJ5aW5nLi4uIgogICAgICAgIGxldCBSRVRSSUVTLT0xCiAgICAgICAgc2xlZXAgNQogICAgZmkKZG9uZQoKdHJhcCAiY2xlYW51cCIgRVhJVAoKY2xlYW51cCgpIHsKICBheiBsb2dvdXQKICBbWyAiXCRURU1QX0RJUiIgPX4gL3RtcC8uKyBdXSAmJiBybSAtcmYgXCRURU1QX0RJUgogIFtbICJcJEFaVVJFX0NPTkZJR19ESVIiID1+IC90bXAvLisgXV0gJiYgcm0gLXJmIFwkQVpVUkVfQ09ORklHX0RJUgp9CgppZiBbICJcJENPTVBPTkVOVCIgPSAibWRtIiBdOyB0aGVuCiAgQ1VSUkVOVF9DRVJUX0ZJTEU9Ii9ldGMvbWRtLnBlbSIKZWxpZiBbICJcJENPTVBPTkVOVCIgPSAibWRzZCIgXTsgdGhlbgogIENVUlJFTlRfQ0VSVF9GSUxFPSIvdmFyL2xpYi93YWFnZW50L01pY3Jvc29mdC5BenVyZS5LZXlWYXVsdC5TdG9yZS9tZHNkLnBlbSIKZWxzZQogIGVjaG8gSW52YWxpZCB1c2FnZSAmJiBleGl0IDEKZmkKClNFQ1JFVF9OQU1FPSJycC1cJHtDT01QT05FTlR9IgpORVdfQ0VSVF9GSUxFPSJcJFRFTVBfRElSL1wkQ09NUE9ORU5ULnBlbSIKZm9yIGF0dGVtcHQgaW4gezEuLjV9OyBkbwogIGF6IGtleXZhdWx0IHNlY3JldCBkb3dubG9hZCAtLWZpbGUgXCRORVdfQ0VSVF9GSUxFIC0taWQgImh0dHBzOi8vJEtFWVZBVUxUUFJFRklYLXN2Yy4kS0VZVkFVTFRETlNTVUZGSVgvc2VjcmV0cy9cJFNFQ1JFVF9OQU1FIiAmJiBicmVhawogIGlmIFtbIFwkYXR0ZW1wdCAtbHQgNSBdXTsgdGhlbiBzbGVlcCAxMDsgZWxzZSBleGl0IDE7IGZpCmRvbmUKCmlmIFsgLWYgXCRORVdfQ0VSVF9GSUxFIF07IHRoZW4KICBpZiBbICJcJENPTVBPTkVOVCIgPSAibWRzZCIgXTsgdGhlbgogICAgY2hvd24gc3lzbG9nOnN5c2xvZyBcJE5FV19DRVJUX0ZJTEUKICBlbHNlCiAgICBzZWQgLWkgLW5lICcxLC9FTkQgQ0VSVElGSUNBVEUvIHAnIFwkTkVXX0NFUlRfRklMRQogIGZpCgogIG5ld19jZXJ0X3NuPSJcJChvcGVuc3NsIHg1MDkgLWluICJcJE5FV19DRVJUX0ZJTEUiIC1ub291dCAtc2VyaWFsIHwgYXdrIC1GPSAne3ByaW50IFwkMn0nKSIKICBjdXJyZW50X2NlcnRfc249IlwkKG9wZW5zc2wgeDUwOSAtaW4gIlwkQ1VSUkVOVF9DRVJUX0ZJTEUiIC1ub291dCAtc2VyaWFsIHwgYXdrIC1GPSAne3ByaW50IFwkMn0nKSIKICBpZiBbWyAhIC16IFwkbmV3X2NlcnRfc24gXV0gJiYgW1sgXCRuZXdfY2VydF9zbiAhPSAiXCRjdXJyZW50X2NlcnRfc24iIF1dOyB0aGVuCiAgICBlY2hvIHVwZGF0aW5nIGNlcnRpZmljYXRlIGZvciBcJENPTVBPTkVOVAogICAgY2htb2QgMDYwMCBcJE5FV19DRVJUX0ZJTEUKICAgIG12IFwkTkVXX0NFUlRfRklMRSBcJENVUlJFTlRfQ0VSVF9GSUxFCiAgZmkKZWxzZQogIGVjaG8gRmFpbGVkIHRvIHJlZnJlc2ggY2VydGlmaWNhdGUgZm9yIFwkQ09NUE9ORU5UICYmIGV4aXQgMQpmaQpFT0YKCmNobW9kIHUreCAvdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaAoKc3lzdGVtY3RsIGVuYWJsZSBkb3dubG9hZC1tZHNkLWNyZWRlbnRpYWxzLnRpbWVyCnN5c3RlbWN0bCBlbmFibGUgZG93bmxvYWQtbWRtLWNyZWRlbnRpYWxzLnRpbWVyCgovdXNyL2xvY2FsL2Jpbi9kb3dubG9hZC1jcmVkZW50aWFscy5zaCBtZHNkCi91c3IvbG9jYWwvYmluL2Rvd25sb2FkLWNyZWRlbnRpYWxzLnNoIG1kbQpNRFNEQ0VSVElGSUNBVEVTQU49JChvcGVuc3NsIHg1MDkgLWluIC92YXIvbGliL3dhYWdlbnQvTWljcm9zb2Z0LkF6dXJlLktleVZhdWx0LlN0b3JlL21kc2QucGVtIC1ub291dCAtc3ViamVjdCB8IHNlZCAtZSAncy8uKkNOID0gLy8nKQoKY2F0ID4vZXRjL3N5c3RlbWQvc3lzdGVtL3dhdGNoLW1kbS1jcmVkZW50aWFscy5zZXJ2aWNlIDw8RU9GCltVbml0XQpEZXNjcmlwdGlvbj1XYXRjaCBmb3IgY2hhbmdlcyBpbiBtZG0ucGVtIGFuZCByZXN0YXJ0cyB0aGUgbWRtIHNlcnZpY2UKCltTZXJ2aWNlXQpUeXBlPW9uZXNob3QKRXhlY1N0YXJ0PS91c3IvYmluL3N5c3RlbWN0bCByZXN0YXJ0IG1kbS5zZXJ2aWNlCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQKRU9GCgpjYXQgPi9ldGMvc3lzdGVtZC9zeXN0ZW0vd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGggPDxFT0YKW1BhdGhdClBhdGhNb2RpZmllZD0vZXRjL21kbS5wZW0KCltJbnN0YWxsXQpXYW50ZWRCeT1tdWx0aS11c2VyLnRhcmdldApFT0YKCnN5c3RlbWN0bCBlbmFibGUgd2F0Y2gtbWRtLWNyZWRlbnRpYWxzLnBhdGgKc3lzdGVtY3RsIHN0YXJ0IHdhdGNoLW1kbS1jcmVkZW50aWFscy5wYXRoCgpta2RpciAvZXRjL3N5c3RlbWQvc3lzdGVtL21kc2Quc2VydmljZS5kCmNhdCA+L2V0Yy9zeXN0ZW1kL3N5c3RlbS9tZHNkLnNlcnZpY2UuZC9vdmVycmlkZS5jb25mIDw8J0VPRicKW1VuaXRdCkFmdGVyPW5ldHdvcmstb25saW5lLnRhcmdldApFT0YKCmNhdCA+L2V0Yy9kZWZhdWx0L21kc2QgPDxFT0YKTURTRF9ST0xFX1BSRUZJWD0vdmFyL3J1bi9tZHNkL2RlZmF1bHQKTURTRF9PUFRJT05TPSItQSAtZCAtciBcJE1EU0RfUk9MRV9QUkVGSVgiCgpleHBvcnQgTU9OSVRPUklOR19HQ1NfRU5WSVJPTk1FTlQ9JyRNRFNERU5WSVJPTk1FTlQnCmV4cG9ydCBNT05JVE9SSU5HX0dDU19BQ0NPVU5UPSckUlBNRFNEQUNDT1VOVCcKZXhwb3J0IE1PTklUT1JJTkdfR0NTX1JFR0lPTj0nJExPQ0FUSU9OJwpleHBvcnQgTU9OSVRPUklOR19HQ1NfQVVUSF9JRF9UWVBFPUF1dGhLZXlWYXVsdApleHBvcnQgTU9OSVRPUklOR19HQ1NfQVVUSF9JRD0nJE1EU0RDRVJUSUZJQ0FURVNBTicKZXhwb3J0IE1PTklUT1JJTkdfR0NTX05BTUVTUEFDRT0nJFJQTURTRE5BTUVTUEFDRScKZXhwb3J0IE1PTklUT1JJTkdfQ09ORklHX1ZFUlNJT049JyRSUE1EU0RDT05GSUdWRVJTSU9OJwpleHBvcnQgTU9OSVRPUklOR19VU0VfR0VORVZBX0NPTkZJR19TRVJWSUNFPXRydWUKCmV4cG9ydCBNT05JVE9SSU5HX1RFTkFOVD0nJExPQ0FUSU9OJwpleHBvcnQgTU9OSVRPUklOR19ST0xFPXJwCmV4cG9ydCBNT05JVE9SSU5HX1JPTEVfSU5TVEFOQ0U9JyQoaG9zdG5hbWUpJwoKZXhwb3J0IE1EU0RfTVNHUEFDS19TT1JUX0NPTFVNTlM9MQpFT0YKCiMgc2V0dGluZyBNT05JVE9SSU5HX0dDU19BVVRIX0lEX1RZUEU9QXV0aEtleVZhdWx0IHNlZW1zIHRvIGhhdmUgY2F1c2VkIG1kc2Qgbm90CiMgdG8gaG9ub3VyIFNTTF9DRVJUX0ZJTEUgYW55IG1vcmUsIGhlYXZlbiBvbmx5IGtub3dzIHdoeS4KbWtkaXIgLXAgL3Vzci9saWIvc3NsL2NlcnRzCmNzcGxpdCAtZiAvdXNyL2xpYi9zc2wvY2VydHMvY2VydC0gLWIgJTAzZC5wZW0gL2V0Yy9wa2kvdGxzL2NlcnRzL2NhLWJ1bmRsZS5jcnQgL14kLzEgeyp9ID4vZGV2L251bGwKY19yZWhhc2ggL3Vzci9saWIvc3NsL2NlcnRzCgojIHdlIGxlYXZlIGNsaWVudElkIGJsYW5rIGFzIGxvbmcgYXMgb25seSAxIG1hbmFnZWQgaWRlbnRpdHkgYXNzaWduZWQgdG8gdm1zcwojIGlmIHdlIGhhdmUgbW9yZSB0aGFuIDEsIHdlIHdpbGwgbmVlZCB0byBwb3B1bGF0ZSB3aXRoIGNsaWVudElkIHVzZWQgZm9yIG9mZi1ub2RlIHNjYW5uaW5nCmNhdCA+L2V0Yy9kZWZhdWx0L3ZzYS1ub2Rlc2Nhbi1hZ2VudC5jb25maWcgPDxFT0YKewogICAgIk5pY2UiOiAxOSwKICAgICJUaW1lb3V0IjogMTA4MDAsCiAgICAiQ2xpZW50SWQiOiAiIiwKICAgICJUZW5hbnRJZCI6ICIkQVpVUkVTRUNQQUNLVlNBVEVOQU5USUQiLAogICAgIlF1YWx5c1N0b3JlQmFzZVVybCI6ICIkQVpVUkVTRUNQQUNLUVVBTFlTVVJMIiwKICAgICJQcm9jZXNzVGltZW91dCI6IDMwMCwKICAgICJDb21tYW5kRGVsYXkiOiAwCiAgfQpFT0YKCmVjaG8gImVuYWJsaW5nIGFybyBzZXJ2aWNlcyIKZm9yIHNlcnZpY2UgaW4gYXJvLW1vbml0b3IgYXJvLXBvcnRhbCBhcm8tcnAgYXVvbXMgYXpzZWNkIGF6c2VjbW9uZCBtZHNkIG1kbSBjaHJvbnlkIGZsdWVudGJpdDsgZG8KICBzeXN0ZW1jdGwgZW5hYmxlICRzZXJ2aWNlLnNlcnZpY2UKZG9uZQoKZm9yIHNjYW4gaW4gYmFzZWxpbmUgY2xhbWF2IHNvZnR3YXJlOyBkbwogIC91c3IvbG9jYWwvYmluL2F6c2VjZCBjb25maWcgLXMgJHNjYW4gLWQgUDFECmRvbmUKCmVjaG8gInJlYm9vdGluZyIKcmVzdG9yZWNvbiAtUkYgL3Zhci9sb2cvKgooc2xlZXAgMzA7IHJlYm9vdCkgJgo=')))]" } } } @@ -530,7 +466,6 @@ "dependsOn": [ "[resourceId('Microsoft.Authorization/roleAssignments', guid(resourceGroup().id, parameters('rpServicePrincipalId'), 'RP / Reader'))]", "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb')]", - "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb-internal')]", "[resourceId('Microsoft.Storage/storageAccounts', substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')))]" ] }, @@ -669,21 +604,6 @@ "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb')]" ] }, - { - "properties": { - "allowVirtualNetworkAccess": true, - "allowForwardedTraffic": true, - "allowGatewayTransit": false, - "useRemoteGateways": false, - "remoteVirtualNetwork": { - "id": "[resourceId(parameters('gatewayResourceGroupName'), 'Microsoft.Network/virtualNetworks', 'gateway-vnet')]" - } - }, - "name": "rp-vnet/peering-gateway-vnet", - "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2020-08-01", - "location": "[resourceGroup().location]" - }, { "properties": {}, "name": "[concat(resourceGroup().location, '.', parameters('clusterParentDomainName'))]", @@ -1020,6 +940,120 @@ }, "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers" }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), 'ARO')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), 'ARO', 'Subscriptions')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', 'ARO', '/Subscriptions/renewLease')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "renewLease", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), 'ARO')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), 'ARO', 'Subscriptions')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', 'ARO', '/Subscriptions/retryLater')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 600;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "retryLater", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), 'ARO')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), 'ARO', 'Billing')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', 'ARO', '/Billing/setCreationBillingTimeStamp')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tvar now = Math.floor(date.getTime() / 1000);\n\t\t\t\tvar billingBody = body[\"billing\"];\n\t\t\t\tif (!billingBody[\"creationTime\"]) {\n\t\t\t\t\tbillingBody[\"creationTime\"] = now;\n\t\t\t\t}\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "setCreationBillingTimeStamp", + "triggerOperation": "Create", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), 'ARO')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), 'ARO', 'Billing')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', 'ARO', '/Billing/setDeletionBillingTimeStamp')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tvar now = Math.floor(date.getTime() / 1000);\n\t\t\t\tvar billingBody = body[\"billing\"];\n\t\t\t\tif (!billingBody[\"creationTime\"]) {\n\t\t\t\t\tbillingBody[\"creationTime\"] = now;\n\t\t\t\t}\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "setDeletionBillingTimeStamp", + "triggerOperation": "Replace", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), 'ARO')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), 'ARO', 'OpenShiftClusters')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', 'ARO', '/OpenShiftClusters/renewLease')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "renewLease", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, + { + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), 'ARO')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), 'ARO', 'Monitors')]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(parameters('databaseAccountName'), '/', 'ARO', '/Monitors/renewLease')]", + "properties": { + "resource": { + "body": "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}", + "id": "renewLease", + "triggerOperation": "All", + "triggerType": "Pre" + } + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers" + }, { "properties": { "severity": 3, @@ -1081,6 +1115,34 @@ "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" ] }, + { + "name": "[concat(parameters('databaseAccountName'), '/', guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('rpServicePrincipalId'), 'DocumentDB Data Contributor'))]", + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "properties": { + "scope": "[concat(resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName')), '/dbs/', 'ARO')]", + "roleDefinitionId": "[concat(resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName')), '/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002')]", + "principalId": "[parameters('rpServicePrincipalId')]", + "principalType": "ServicePrincipal" + }, + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ] + }, + { + "name": "[concat(parameters('databaseAccountName'), '/', guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('gatewayServicePrincipalId'), 'DocumentDB Data Contributor'))]", + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "properties": { + "scope": "[concat(resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName')), '/dbs/', 'ARO')]", + "roleDefinitionId": "[concat(resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName')), '/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002')]", + "principalId": "[parameters('gatewayServicePrincipalId')]", + "principalType": "ServicePrincipal" + }, + "apiVersion": "2023-04-15", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + ] + }, { "name": "[guid(resourceGroup().id, parameters('rpServicePrincipalId'), 'RP / Reader')]", "type": "Microsoft.Authorization/roleAssignments", diff --git a/pkg/deploy/config.go b/pkg/deploy/config.go index 998d11095e8..40ca31f7366 100644 --- a/pkg/deploy/config.go +++ b/pkg/deploy/config.go @@ -55,10 +55,8 @@ type Configuration struct { ClusterParentDomainName *string `json:"clusterParentDomainName,omitempty" value:"required"` DatabaseAccountName *string `json:"databaseAccountName,omitempty" value:"required"` CosmosDB *CosmosDBConfiguration `json:"cosmosDB,omitempty"` - DBTokenClientID *string `json:"dbtokenClientId,omitempty" value:"required"` DisableCosmosDBFirewall *bool `json:"disableCosmosDBFirewall,omitempty"` ExtraClusterKeyvaultAccessPolicies []interface{} `json:"extraClusterKeyvaultAccessPolicies,omitempty" value:"required"` - ExtraDBTokenKeyvaultAccessPolicies []interface{} `json:"extraDBTokenKeyvaultAccessPolicies,omitempty" value:"required"` ExtraCosmosDBIPs []string `json:"extraCosmosDBIPs,omitempty"` ExtraGatewayKeyvaultAccessPolicies []interface{} `json:"extraGatewayKeyvaultAccessPolicies,omitempty" value:"required"` ExtraPortalKeyvaultAccessPolicies []interface{} `json:"extraPortalKeyvaultAccessPolicies,omitempty" value:"required"` diff --git a/pkg/deploy/deploy.go b/pkg/deploy/deploy.go index 07ab5f42da5..a6cebf2df9a 100644 --- a/pkg/deploy/deploy.go +++ b/pkg/deploy/deploy.go @@ -49,7 +49,6 @@ type deployer struct { globalaccounts storage.AccountsClient deployments features.DeploymentsClient groups features.ResourceGroupsClient - loadbalancers network.LoadBalancersClient userassignedidentities msi.UserAssignedIdentitiesClient providers features.ProvidersClient publicipaddresses network.PublicIPAddressesClient @@ -59,7 +58,6 @@ type deployer struct { vmssvms compute.VirtualMachineScaleSetVMsClient zones dns.ZonesClient clusterKeyvault keyvault.Manager - dbtokenKeyvault keyvault.Manager portalKeyvault keyvault.Manager serviceKeyvault keyvault.Manager @@ -101,7 +99,6 @@ func New(ctx context.Context, log *logrus.Entry, _env env.Core, config *RPConfig globalaccounts: storage.NewAccountsClient(_env.Environment(), *config.Configuration.GlobalSubscriptionID, authorizer), deployments: features.NewDeploymentsClient(_env.Environment(), config.SubscriptionID, authorizer), groups: features.NewResourceGroupsClient(_env.Environment(), config.SubscriptionID, authorizer), - loadbalancers: network.NewLoadBalancersClient(_env.Environment(), config.SubscriptionID, authorizer), userassignedidentities: msi.NewUserAssignedIdentitiesClient(_env.Environment(), config.SubscriptionID, authorizer), providers: features.NewProvidersClient(_env.Environment(), config.SubscriptionID, authorizer), roleassignments: authorization.NewRoleAssignmentsClient(_env.Environment(), config.SubscriptionID, authorizer), @@ -111,7 +108,6 @@ func New(ctx context.Context, log *logrus.Entry, _env env.Core, config *RPConfig vmssvms: compute.NewVirtualMachineScaleSetVMsClient(_env.Environment(), config.SubscriptionID, authorizer), zones: dns.NewZonesClient(_env.Environment(), config.SubscriptionID, authorizer), clusterKeyvault: keyvault.NewManager(kvAuthorizer, "https://"+*config.Configuration.KeyvaultPrefix+env.ClusterKeyvaultSuffix+"."+_env.Environment().KeyVaultDNSSuffix+"/"), - dbtokenKeyvault: keyvault.NewManager(kvAuthorizer, "https://"+*config.Configuration.KeyvaultPrefix+env.DBTokenKeyvaultSuffix+"."+_env.Environment().KeyVaultDNSSuffix+"/"), portalKeyvault: keyvault.NewManager(kvAuthorizer, "https://"+*config.Configuration.KeyvaultPrefix+env.PortalKeyvaultSuffix+"."+_env.Environment().KeyVaultDNSSuffix+"/"), serviceKeyvault: keyvault.NewManager(kvAuthorizer, "https://"+*config.Configuration.KeyvaultPrefix+env.ServiceKeyvaultSuffix+"."+_env.Environment().KeyVaultDNSSuffix+"/"), diff --git a/pkg/deploy/deploy_gateway.go b/pkg/deploy/deploy_gateway.go index 47665c81e9a..6bb19c9a8e7 100644 --- a/pkg/deploy/deploy_gateway.go +++ b/pkg/deploy/deploy_gateway.go @@ -40,9 +40,6 @@ func (d *deployer) DeployGateway(ctx context.Context) error { // Special cases where the config isn't marshalled into the ARM template parameters cleanly parameters := d.getParameters(template["parameters"].(map[string]interface{})) - parameters.Parameters["dbtokenURL"] = &arm.ParametersParameter{ - Value: "https://dbtoken." + d.config.Location + "." + *d.config.Configuration.RPParentDomainName + ":8445", - } parameters.Parameters["rpImage"] = &arm.ParametersParameter{ Value: *d.config.Configuration.RPImagePrefix + ":" + d.version, } diff --git a/pkg/deploy/deploy_rp.go b/pkg/deploy/deploy_rp.go index 72e309abb5e..5c36e987b88 100644 --- a/pkg/deploy/deploy_rp.go +++ b/pkg/deploy/deploy_rp.go @@ -114,12 +114,6 @@ func (d *deployer) configureDNS(ctx context.Context) error { return err } - lb, err := d.loadbalancers.Get(ctx, d.config.RPResourceGroupName, "rp-lb-internal", "") - if err != nil { - return err - } - dbtokenIP := *((*lb.FrontendIPConfigurations)[0].PrivateIPAddress) - zone, err := d.zones.Get(ctx, d.config.RPResourceGroupName, d.config.Location+"."+*d.config.Configuration.ClusterParentDomainName) if err != nil { return err @@ -153,20 +147,6 @@ func (d *deployer) configureDNS(ctx context.Context) error { return err } - _, err = d.globalrecordsets.CreateOrUpdate(ctx, *d.config.Configuration.GlobalResourceGroupName, *d.config.Configuration.RPParentDomainName, "dbtoken."+d.config.Location, mgmtdns.A, mgmtdns.RecordSet{ - RecordSetProperties: &mgmtdns.RecordSetProperties{ - TTL: to.Int64Ptr(3600), - ARecords: &[]mgmtdns.ARecord{ - { - Ipv4Address: &dbtokenIP, - }, - }, - }, - }, "", "") - if err != nil { - return err - } - nsRecords := make([]mgmtdns.NsRecord, 0, len(*zone.NameServers)) for i := range *zone.NameServers { nsRecords = append(nsRecords, mgmtdns.NsRecord{ diff --git a/pkg/deploy/devconfig.go b/pkg/deploy/devconfig.go index d5d17d8b8ce..b8f867ab0ed 100644 --- a/pkg/deploy/devconfig.go +++ b/pkg/deploy/devconfig.go @@ -122,14 +122,10 @@ func DevConfig(_env env.Core) (*Config, error) { PortalProvisionedThroughput: 400, GatewayProvisionedThroughput: 400, }, - DBTokenClientID: to.StringPtr(os.Getenv("AZURE_DBTOKEN_CLIENT_ID")), DisableCosmosDBFirewall: to.BoolPtr(true), ExtraClusterKeyvaultAccessPolicies: []interface{}{ adminKeyvaultAccessPolicy(_env), }, - ExtraDBTokenKeyvaultAccessPolicies: []interface{}{ - adminKeyvaultAccessPolicy(_env), - }, ExtraGatewayKeyvaultAccessPolicies: []interface{}{ adminKeyvaultAccessPolicy(_env), }, @@ -157,9 +153,6 @@ func DevConfig(_env env.Core) (*Config, error) { "qos.ppe.warm.ingest.monitor.core.windows.net", "test1.diagnostics.monitoring.core.windows.net", }, - GatewayFeatures: []string{ - "InsecureSkipVerifyDBTokenCertificate", - }, GatewayMDSDConfigVersion: to.StringPtr(version.DevGatewayGenevaLoggingConfigVersion), GatewayVMSSCapacity: to.IntPtr(1), GlobalResourceGroupLocation: to.StringPtr(_env.Location()), diff --git a/pkg/deploy/generator/const.go b/pkg/deploy/generator/const.go index c11b7bed547..caa6546a805 100644 --- a/pkg/deploy/generator/const.go +++ b/pkg/deploy/generator/const.go @@ -31,4 +31,9 @@ const ( // Tag constants tagKeyExemptPublicBlob = "Az.Sec.AnonymousBlobAccessEnforcement::Skip" tagValueExemptPublicBlob = "PublicRelease" + + renewLeaseTriggerFunction = "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 60;\n\t\t\t\trequest.setBody(body);\n\t\t\t}" + retryLaterTriggerFunction = "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tbody[\"leaseExpires\"] = Math.floor(date.getTime() / 1000) + 600;\n\t\t\t\trequest.setBody(body);\n\t\t\t}" + setCreationBillingTimeStampTriggerFunction = "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tvar now = Math.floor(date.getTime() / 1000);\n\t\t\t\tvar billingBody = body[\"billing\"];\n\t\t\t\tif (!billingBody[\"creationTime\"]) {\n\t\t\t\t\tbillingBody[\"creationTime\"] = now;\n\t\t\t\t}\n\t\t\t\trequest.setBody(body);\n\t\t\t}" + setDeletionBillingTimeStampTriggerFunction = "function trigger() {\n\t\t\t\tvar request = getContext().getRequest();\n\t\t\t\tvar body = request.getBody();\n\t\t\t\tvar date = new Date();\n\t\t\t\tvar now = Math.floor(date.getTime() / 1000);\n\t\t\t\tvar billingBody = body[\"billing\"];\n\t\t\t\tif (!billingBody[\"creationTime\"]) {\n\t\t\t\t\tbillingBody[\"creationTime\"] = now;\n\t\t\t\t}\n\t\t\t\trequest.setBody(body);\n\t\t\t}" ) diff --git a/pkg/deploy/generator/resources_gateway.go b/pkg/deploy/generator/resources_gateway.go index b0327216211..84ca2766999 100644 --- a/pkg/deploy/generator/resources_gateway.go +++ b/pkg/deploy/generator/resources_gateway.go @@ -199,8 +199,6 @@ func (g *generator) gatewayVMSS() *arm.Resource { "azureSecPackQualysUrl", "azureSecPackVSATenantId", "databaseAccountName", - "dbtokenClientId", - "dbtokenUrl", "mdmFrontendUrl", "mdsdEnvironment", "fluentbitImage", diff --git a/pkg/deploy/generator/resources_rp.go b/pkg/deploy/generator/resources_rp.go index 1813c98fa9c..f13a37de3f2 100644 --- a/pkg/deploy/generator/resources_rp.go +++ b/pkg/deploy/generator/resources_rp.go @@ -99,32 +99,6 @@ func (g *generator) rpSecurityGroup() *arm.Resource { }) } else { rules = append(rules, - mgmtnetwork.SecurityRule{ - SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ - Protocol: mgmtnetwork.SecurityRuleProtocolTCP, - SourcePortRange: to.StringPtr("*"), - DestinationPortRange: to.StringPtr("445"), - SourceAddressPrefix: to.StringPtr("10.0.8.0/24"), - DestinationAddressPrefix: to.StringPtr("*"), - Access: mgmtnetwork.SecurityRuleAccessAllow, - Priority: to.Int32Ptr(140), - Direction: mgmtnetwork.SecurityRuleDirectionInbound, - }, - Name: to.StringPtr("dbtoken_in_gateway_445"), - }, - mgmtnetwork.SecurityRule{ - SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ - Protocol: mgmtnetwork.SecurityRuleProtocolTCP, - SourcePortRange: to.StringPtr("*"), - DestinationPortRange: to.StringPtr("8445"), - SourceAddressPrefix: to.StringPtr("10.0.8.0/24"), - DestinationAddressPrefix: to.StringPtr("*"), - Access: mgmtnetwork.SecurityRuleAccessAllow, - Priority: to.Int32Ptr(141), - Direction: mgmtnetwork.SecurityRuleDirectionInbound, - }, - Name: to.StringPtr("dbtoken_in_gateway_8445"), - }, mgmtnetwork.SecurityRule{ SecurityRulePropertiesFormat: &mgmtnetwork.SecurityRulePropertiesFormat{ Protocol: mgmtnetwork.SecurityRuleProtocolTCP, @@ -334,69 +308,6 @@ func (g *generator) rpLB() *arm.Resource { } } -func (g *generator) rpLBInternal() *arm.Resource { - return &arm.Resource{ - Resource: &mgmtnetwork.LoadBalancer{ - Sku: &mgmtnetwork.LoadBalancerSku{ - Name: mgmtnetwork.LoadBalancerSkuNameStandard, - }, - LoadBalancerPropertiesFormat: &mgmtnetwork.LoadBalancerPropertiesFormat{ - FrontendIPConfigurations: &[]mgmtnetwork.FrontendIPConfiguration{ - { - FrontendIPConfigurationPropertiesFormat: &mgmtnetwork.FrontendIPConfigurationPropertiesFormat{ - Subnet: &mgmtnetwork.Subnet{ - ID: to.StringPtr("[resourceId('Microsoft.Network/virtualNetworks/subnets', 'rp-vnet', 'rp-subnet')]"), - }, - }, - Name: to.StringPtr("dbtoken-frontend"), - Zones: &[]string{}, - }, - }, - BackendAddressPools: &[]mgmtnetwork.BackendAddressPool{ - { - Name: to.StringPtr("rp-backend"), - }, - }, - LoadBalancingRules: &[]mgmtnetwork.LoadBalancingRule{ - { - LoadBalancingRulePropertiesFormat: &mgmtnetwork.LoadBalancingRulePropertiesFormat{ - FrontendIPConfiguration: &mgmtnetwork.SubResource{ - ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', 'rp-lb-internal', 'dbtoken-frontend')]"), - }, - BackendAddressPool: &mgmtnetwork.SubResource{ - ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb-internal', 'rp-backend')]"), - }, - Probe: &mgmtnetwork.SubResource{ - ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/probes', 'rp-lb-internal', 'dbtoken-probe')]"), - }, - Protocol: mgmtnetwork.TransportProtocolTCP, - LoadDistribution: mgmtnetwork.LoadDistributionDefault, - FrontendPort: to.Int32Ptr(8445), - BackendPort: to.Int32Ptr(445), - }, - Name: to.StringPtr("dbtoken-lbrule"), - }, - }, - Probes: &[]mgmtnetwork.Probe{ - { - ProbePropertiesFormat: &mgmtnetwork.ProbePropertiesFormat{ - Protocol: mgmtnetwork.ProbeProtocolHTTPS, - Port: to.Int32Ptr(445), - NumberOfProbes: to.Int32Ptr(2), - RequestPath: to.StringPtr("/healthz/ready"), - }, - Name: to.StringPtr("dbtoken-probe"), - }, - }, - }, - Name: to.StringPtr("rp-lb-internal"), - Type: to.StringPtr("Microsoft.Network/loadBalancers"), - Location: to.StringPtr("[resourceGroup().location]"), - }, - APIVersion: azureclient.APIVersion("Microsoft.Network"), - } -} - // rpLBAlert generates an alert resource for the rp-lb healthprobe metric func (g *generator) rpLBAlert(threshold float64, severity int32, name string, evalFreq string, windowSize string, metric string) *arm.Resource { return &arm.Resource{ @@ -463,7 +374,6 @@ func (g *generator) rpVMSS() *arm.Resource { "clusterMdsdNamespace", "clusterParentDomainName", "databaseAccountName", - "dbtokenClientId", "fluentbitImage", "fpClientId", "fpServicePrincipalId", @@ -604,9 +514,6 @@ func (g *generator) rpVMSS() *arm.Resource { { ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb', 'rp-backend')]"), }, - { - ID: to.StringPtr("[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', 'rp-lb-internal', 'rp-backend')]"), - }, }, }, }, @@ -654,7 +561,6 @@ func (g *generator) rpVMSS() *arm.Resource { DependsOn: []string{ "[resourceId('Microsoft.Authorization/roleAssignments', guid(resourceGroup().id, parameters('rpServicePrincipalId'), 'RP / Reader'))]", "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb')]", - "[resourceId('Microsoft.Network/loadBalancers', 'rp-lb-internal')]", "[resourceId('Microsoft.Storage/storageAccounts', substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')))]", }, } @@ -692,20 +598,6 @@ func (g *generator) rpClusterKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolic } } -func (g *generator) rpDBTokenKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolicyEntry { - return []mgmtkeyvault.AccessPolicyEntry{ - { - TenantID: &tenantUUIDHack, - ObjectID: to.StringPtr("[parameters('rpServicePrincipalId')]"), - Permissions: &mgmtkeyvault.Permissions{ - Secrets: &[]mgmtkeyvault.SecretPermissions{ - mgmtkeyvault.SecretPermissionsGet, - }, - }, - }, - } -} - func (g *generator) rpPortalKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolicyEntry { return []mgmtkeyvault.AccessPolicyEntry{ { @@ -776,53 +668,6 @@ func (g *generator) rpClusterKeyvault() *arm.Resource { } } -func (g *generator) rpDBTokenKeyvault() *arm.Resource { - vault := &mgmtkeyvault.Vault{ - Properties: &mgmtkeyvault.VaultProperties{ - EnableSoftDelete: to.BoolPtr(true), - TenantID: &tenantUUIDHack, - Sku: &mgmtkeyvault.Sku{ - Name: mgmtkeyvault.Standard, - Family: to.StringPtr("A"), - }, - AccessPolicies: &[]mgmtkeyvault.AccessPolicyEntry{ - { - ObjectID: to.StringPtr(dbTokenAccessPolicyHack), - }, - }, - }, - Name: to.StringPtr("[concat(parameters('keyvaultPrefix'), '" + env.DBTokenKeyvaultSuffix + "')]"), - Type: to.StringPtr("Microsoft.KeyVault/vaults"), - Location: to.StringPtr("[resourceGroup().location]"), - } - - if !g.production { - *vault.Properties.AccessPolicies = append(g.rpDBTokenKeyvaultAccessPolicies(), - mgmtkeyvault.AccessPolicyEntry{ - TenantID: &tenantUUIDHack, - ObjectID: to.StringPtr("[parameters('adminObjectId')]"), - Permissions: &mgmtkeyvault.Permissions{ - Certificates: &[]mgmtkeyvault.CertificatePermissions{ - mgmtkeyvault.Delete, - mgmtkeyvault.Get, - mgmtkeyvault.Import, - mgmtkeyvault.List, - }, - Secrets: &[]mgmtkeyvault.SecretPermissions{ - mgmtkeyvault.SecretPermissionsSet, - mgmtkeyvault.SecretPermissionsList, - }, - }, - }, - ) - } - - return &arm.Resource{ - Resource: vault, - APIVersion: azureclient.APIVersion("Microsoft.KeyVault"), - } -} - func (g *generator) rpPortalKeyvault() *arm.Resource { vault := &mgmtkeyvault.Vault{ Properties: &mgmtkeyvault.VaultProperties{ @@ -1029,11 +874,32 @@ func (g *generator) rpCosmosDB() []*arm.Resource { if g.production { rs = append(rs, g.database("'ARO'", true)...) rs = append(rs, g.rpCosmosDBAlert(10, 90, 3, "rp-cosmosdb-alert", "PT5M", "PT1H")) + rs = append(rs, g.CosmosDBDataContributorRoleAssignment("'ARO'", "rp")) + rs = append(rs, g.CosmosDBDataContributorRoleAssignment("'ARO'", "gateway")) } return rs } +func (g *generator) CosmosDBDataContributorRoleAssignment(databaseName, component string) *arm.Resource { + return &arm.Resource{ + Resource: mgmtauthorization.RoleAssignment{ + Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('" + component + "ServicePrincipalId'), 'DocumentDB Data Contributor'))]"), + Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments"), + RoleAssignmentPropertiesWithScope: &mgmtauthorization.RoleAssignmentPropertiesWithScope{ + Scope: to.StringPtr("[concat(resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName')), '/dbs/', " + databaseName + ")]"), + RoleDefinitionID: to.StringPtr("[concat(resourceId('Microsoft.DocumentDB/databaseAccounts/', parameters('databaseAccountName')), '/sqlRoleDefinitions/" + rbac.RoleDocumentDBDataContributor + "')]"), + PrincipalID: to.StringPtr("[parameters('" + component + "ServicePrincipalId')]"), + PrincipalType: mgmtauthorization.ServicePrincipal, + }, + }, + APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), + DependsOn: []string{ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]", + }, + } +} + func (g *generator) database(databaseName string, addDependsOn bool) []*arm.Resource { database := &arm.Resource{ Resource: &sdkcosmos.SQLDatabaseCreateUpdateParameters{ @@ -1340,6 +1206,20 @@ func (g *generator) database(databaseName string, addDependsOn bool) []*arm.Reso }, } + // Adding Triggers + rs = append(rs, + // Subscription + g.rpCosmosDBTriggers(databaseName, "Subscriptions", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), + g.rpCosmosDBTriggers(databaseName, "Subscriptions", "retryLater", retryLaterTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), + // Billing + g.rpCosmosDBTriggers(databaseName, "Billing", "setCreationBillingTimeStamp", setCreationBillingTimeStampTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationCreate), + g.rpCosmosDBTriggers(databaseName, "Billing", "setDeletionBillingTimeStamp", setDeletionBillingTimeStampTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationReplace), + // OpenShiftClusters + g.rpCosmosDBTriggers(databaseName, "OpenShiftClusters", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), + // Monitors + g.rpCosmosDBTriggers(databaseName, "Monitors", "renewLease", renewLeaseTriggerFunction, sdkcosmos.TriggerTypePre, sdkcosmos.TriggerOperationAll), + ) + if addDependsOn { for i := range rs { rs[i].DependsOn = append(rs[i].DependsOn, @@ -1351,6 +1231,30 @@ func (g *generator) database(databaseName string, addDependsOn bool) []*arm.Reso return rs } +func (g *generator) rpCosmosDBTriggers(databaseName, containerName, triggerID, triggerFunction string, triggerType sdkcosmos.TriggerType, triggerOperation sdkcosmos.TriggerOperation) *arm.Resource { + return &arm.Resource{ + Resource: &sdkcosmos.SQLTriggerCreateUpdateParameters{ + Properties: &sdkcosmos.SQLTriggerCreateUpdateProperties{ + Resource: &sdkcosmos.SQLTriggerResource{ + ID: to.StringPtr(triggerID), + Body: to.StringPtr(triggerFunction), + TriggerOperation: &triggerOperation, + TriggerType: &triggerType, + }, + }, + Name: to.StringPtr("[concat(parameters('databaseAccountName'), '/', " + databaseName + ", '/" + containerName + "/" + triggerID + "')]"), + Type: to.StringPtr("Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers"), + Location: to.StringPtr("[resourceGroup().location]"), + }, + APIVersion: azureclient.APIVersion("Microsoft.DocumentDB"), + DependsOn: []string{ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), " + databaseName + ")]", + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), " + databaseName + ", '" + containerName + "')]", + }, + Type: "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/triggers", + } +} + func (g *generator) rpCosmosDBAlert(throttledRequestThreshold float64, ruConsumptionThreshold float64, severity int32, name string, evalFreq string, windowSize string) *arm.Resource { throttledRequestMetricCriteria := mgmtinsights.MetricCriteria{ CriterionType: mgmtinsights.CriterionTypeStaticThresholdCriterion, diff --git a/pkg/deploy/generator/scripts/gatewayVMSS.sh b/pkg/deploy/generator/scripts/gatewayVMSS.sh index 464ca60cd9d..15faeab8e23 100644 --- a/pkg/deploy/generator/scripts/gatewayVMSS.sh +++ b/pkg/deploy/generator/scripts/gatewayVMSS.sh @@ -265,8 +265,6 @@ echo "configuring aro-gateway service" cat >/etc/sysconfig/aro-gateway </etc/sysconfig/aro-dbtoken </etc/systemd/system/aro-dbtoken.service <<'EOF' -[Unit] -After=network-online.target -Wants=network-online.target - -[Service] -EnvironmentFile=/etc/sysconfig/aro-dbtoken -ExecStartPre=-/usr/bin/docker rm -f %N -ExecStart=/usr/bin/docker run \ - --hostname %H \ - --name %N \ - --rm \ - --cap-drop net_raw \ - -e AZURE_GATEWAY_SERVICE_PRINCIPAL_ID \ - -e DATABASE_ACCOUNT_NAME \ - -e AZURE_DBTOKEN_CLIENT_ID \ - -e KEYVAULT_PREFIX \ - -e MDM_ACCOUNT \ - -e MDM_NAMESPACE \ - -m 2g \ - -p 445:8445 \ - -v /run/systemd/journal:/run/systemd/journal \ - -v /var/etw:/var/etw:z \ - $RPIMAGE \ - dbtoken -ExecStop=/usr/bin/docker stop -t 3600 %N -TimeoutStopSec=3600 -Restart=always -RestartSec=1 -StartLimitInterval=0 - -[Install] -WantedBy=multi-user.target -EOF - # DOMAIN_NAME, CLUSTER_MDSD_ACCOUNT, CLUSTER_MDSD_CONFIG_VERSION, GATEWAY_DOMAINS, GATEWAY_RESOURCEGROUP, MDSD_ENVIRONMENT CLUSTER_MDSD_NAMESPACE # are not used, but can't easily be refactored out. Should be revisited in the future. echo "configuring aro-monitor service" @@ -678,7 +631,7 @@ cat >/etc/default/vsa-nodescan-agent.config <=0.3 due to deploys going down to 32% at times. g.rpLBAlert(67.0, 3, "rp-degraded-alert", "PT15M", "PT6H", "DipAvailability"), // 1/3 backend down for 1h or 2/3 down for 3h in the last 6h g.rpLBAlert(33.0, 2, "rp-vnet-alert", "PT5M", "PT5M", "VipAvailability")) // this will trigger only if the Azure network infrastructure between the loadBalancers and VMs is down for 3.5min // more on alerts https://msazure.visualstudio.com/AzureRedHatOpenShift/_wiki/wikis/ARO.wiki/53765/WIP-Alerting - - t.Resources = append(t.Resources, - g.virtualNetworkPeering("rp-vnet/peering-gateway-vnet", "[resourceId(parameters('gatewayResourceGroupName'), 'Microsoft.Network/virtualNetworks', 'gateway-vnet')]", false, false, nil), - ) } t.Resources = append(t.Resources, g.rpDNSZone(), @@ -282,7 +276,6 @@ func (g *generator) rpPredeployTemplate() *arm.Template { if g.production { t.Variables = map[string]interface{}{ "clusterKeyvaultAccessPolicies": g.rpClusterKeyvaultAccessPolicies(), - "dbTokenKeyvaultAccessPolicies": g.rpDBTokenKeyvaultAccessPolicies(), "portalKeyvaultAccessPolicies": g.rpPortalKeyvaultAccessPolicies(), "serviceKeyvaultAccessPolicies": g.rpServiceKeyvaultAccessPolicies(), } @@ -298,7 +291,6 @@ func (g *generator) rpPredeployTemplate() *arm.Template { params = append(params, "deployNSGs", "extraClusterKeyvaultAccessPolicies", - "extraDBTokenKeyvaultAccessPolicies", "extraPortalKeyvaultAccessPolicies", "extraServiceKeyvaultAccessPolicies", "gatewayResourceGroupName", @@ -317,7 +309,6 @@ func (g *generator) rpPredeployTemplate() *arm.Template { p.Type = "bool" p.DefaultValue = false case "extraClusterKeyvaultAccessPolicies", - "extraDBTokenKeyvaultAccessPolicies", "extraPortalKeyvaultAccessPolicies", "extraServiceKeyvaultAccessPolicies": p.Type = "array" @@ -337,7 +328,6 @@ func (g *generator) rpPredeployTemplate() *arm.Template { g.rpVnet(), g.rpPEVnet(), g.rpClusterKeyvault(), - g.rpDBTokenKeyvault(), g.rpPortalKeyvault(), g.rpServiceKeyvault(), g.rpServiceKeyvaultDynamic(), diff --git a/pkg/env/core.go b/pkg/env/core.go index c5dde953a2c..b24bc05b52a 100644 --- a/pkg/env/core.go +++ b/pkg/env/core.go @@ -22,7 +22,6 @@ const ( COMPONENT_RP ServiceComponent = "RP" COMPONENT_GATEWAY ServiceComponent = "GATEWAY" COMPONENT_MONITOR ServiceComponent = "MONITOR" - COMPONENT_DBTOKEN ServiceComponent = "DBTOKEN" COMPONENT_OPERATOR ServiceComponent = "OPERATOR" COMPONENT_MIRROR ServiceComponent = "MIRROR" COMPONENT_PORTAL ServiceComponent = "PORTAL" diff --git a/pkg/env/env.go b/pkg/env/env.go index faed9b08cae..13da5178976 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -46,13 +46,11 @@ const ( EncryptionSecretV2Name = "encryption-key-v2" FrontendEncryptionSecretName = "fe-encryption-key" FrontendEncryptionSecretV2Name = "fe-encryption-key-v2" - DBTokenServerSecretName = "dbtoken-server" PortalServerSecretName = "portal-server" PortalServerClientSecretName = "portal-client" PortalServerSessionKeySecretName = "portal-session-key" PortalServerSSHKeySecretName = "portal-sshkey" ClusterKeyvaultSuffix = "-cls" - DBTokenKeyvaultSuffix = "-dbt" GatewayKeyvaultSuffix = "-gwy" PortalKeyvaultSuffix = "-por" ServiceKeyvaultSuffix = "-svc" diff --git a/pkg/util/arm/marshal.go b/pkg/util/arm/marshal.go index 69bf5fdd8c4..03cf7f5afac 100644 --- a/pkg/util/arm/marshal.go +++ b/pkg/util/arm/marshal.go @@ -25,6 +25,8 @@ func (r *Resource) MarshalJSON() ([]byte, error) { b, err = r.Resource.(*sdkcosmos.SQLDatabaseCreateUpdateParameters).MarshalJSON() } else if reflect.TypeOf(r.Resource) == reflect.TypeOf(&sdkcosmos.SQLContainerCreateUpdateParameters{}) { b, err = r.Resource.(*sdkcosmos.SQLContainerCreateUpdateParameters).MarshalJSON() + } else if reflect.TypeOf(r.Resource) == reflect.TypeOf(&sdkcosmos.SQLTriggerCreateUpdateParameters{}) { + b, err = r.Resource.(*sdkcosmos.SQLTriggerCreateUpdateParameters).MarshalJSON() } } else if strings.HasPrefix(r.Type, "Microsoft.DocumentDB/databaseAccounts") { b, err = r.Resource.(*sdkcosmos.DatabaseAccountCreateUpdateParameters).MarshalJSON() diff --git a/pkg/util/azureclient/environments.go b/pkg/util/azureclient/environments.go index 564e2be613e..a4d6b385728 100644 --- a/pkg/util/azureclient/environments.go +++ b/pkg/util/azureclient/environments.go @@ -31,9 +31,10 @@ type AROEnvironment struct { Cloud cloud.Configuration // Microsoft identity platform scopes used by ARO // See https://learn.microsoft.com/EN-US/azure/active-directory/develop/scopes-oidc#the-default-scope - ResourceManagerScope string - KeyVaultScope string - MicrosoftGraphScope string + ResourceManagerScope string + KeyVaultScope string + MicrosoftGraphScope string + CosmosDBDNSSuffixScope string } // AzureRbacPDPEnvironment contains cloud specific instance of Authz RBAC PDP Remote Server @@ -59,9 +60,10 @@ var ( Endpoint: "https://%s.authorization.azure.net/providers/Microsoft.Authorization/checkAccess?api-version=2021-06-01-preview", OAuthScope: "https://authorization.azure.net/.default", }, - ResourceManagerScope: azure.PublicCloud.ResourceManagerEndpoint + "/.default", - KeyVaultScope: azure.PublicCloud.ResourceIdentifiers.KeyVault + "/.default", - MicrosoftGraphScope: azure.PublicCloud.MicrosoftGraphEndpoint + "/.default", + ResourceManagerScope: azure.PublicCloud.ResourceManagerEndpoint + "/.default", + KeyVaultScope: azure.PublicCloud.ResourceIdentifiers.KeyVault + "/.default", + MicrosoftGraphScope: azure.PublicCloud.MicrosoftGraphEndpoint + "/.default", + CosmosDBDNSSuffixScope: azure.PublicCloud.CosmosDBDNSSuffix + "/.default", } // USGovernmentCloud contains additional ARO information for the US Gov cloud environment. @@ -81,9 +83,10 @@ var ( Endpoint: "https://%s.authorization.azure.us/providers/Microsoft.Authorization/checkAccess?api-version=2021-06-01-preview", OAuthScope: "https://authorization.azure.us/.default", }, - ResourceManagerScope: azure.USGovernmentCloud.ResourceManagerEndpoint + "/.default", - KeyVaultScope: azure.USGovernmentCloud.ResourceIdentifiers.KeyVault + "/.default", - MicrosoftGraphScope: azure.USGovernmentCloud.MicrosoftGraphEndpoint + "/.default", + ResourceManagerScope: azure.USGovernmentCloud.ResourceManagerEndpoint + "/.default", + KeyVaultScope: azure.USGovernmentCloud.ResourceIdentifiers.KeyVault + "/.default", + MicrosoftGraphScope: azure.USGovernmentCloud.MicrosoftGraphEndpoint + "/.default", + CosmosDBDNSSuffixScope: azure.USGovernmentCloud.CosmosDBDNSSuffix + "/.default", } ) diff --git a/pkg/util/rbac/rbac.go b/pkg/util/rbac/rbac.go index 29ad6c2eca9..314908053a1 100644 --- a/pkg/util/rbac/rbac.go +++ b/pkg/util/rbac/rbac.go @@ -15,6 +15,7 @@ const ( RoleACRPull = "7f951dda-4ed3-4680-a7ca-43fe172d538d" RoleContributor = "b24988ac-6180-42a0-ab88-20f7382dd24c" RoleDocumentDBAccountContributor = "5bd9cd88-fe45-4216-938b-f97437e15450" + RoleDocumentDBDataContributor = "00000000-0000-0000-0000-000000000002" RoleDNSZoneContributor = "befefa01-2a29-4197-83a8-272ff33ce314" RoleNetworkContributor = "4d97b98b-1d4f-4787-a291-c67834d212e7" RoleOwner = "8e3af657-a8ff-443c-a75c-2fe8c4bcb635" diff --git a/vendor/github.com/jewzaam/go-cosmosdb/pkg/gencosmosdb/bindata.go b/vendor/github.com/jewzaam/go-cosmosdb/pkg/gencosmosdb/bindata.go index 58231cdf0ed..910b8ed7191 100644 --- a/vendor/github.com/jewzaam/go-cosmosdb/pkg/gencosmosdb/bindata.go +++ b/vendor/github.com/jewzaam/go-cosmosdb/pkg/gencosmosdb/bindata.go @@ -86,7 +86,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _authorizerGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\x4d\x6b\x1b\x31\x10\x3d\x5b\xbf\x62\xba\x90\xa0\x0d\x1b\xc5\x09\x49\x0e\x06\x1f\x4a\x9b\xb4\xb4\x71\xa0\xb5\x7b\x6a\x7a\x90\xd7\x63\xaf\x88\x57\xda\x8c\x66\xeb\xba\xc6\xff\xbd\xcc\xfa\x33\x89\x29\x14\x6a\x30\x9a\x19\x31\xa3\xf7\xe6\x3d\xb6\xb2\xf9\xa3\x9d\x20\xe4\x21\x96\x21\x8e\x86\x4a\xb9\xb2\x0a\xc4\xa0\x55\x2b\xc9\x69\x5e\x71\x38\x2b\x4a\x9b\x27\xbb\x34\x16\xf6\xe2\xea\x5a\x0a\xe8\xf3\x30\x72\x7e\x72\x36\xb4\x11\xaf\x2f\xa5\x34\x2e\x59\x0e\x8f\x7c\x56\x30\x57\x9b\xb8\xa6\xa9\x84\x91\xc9\xf9\x49\x94\x90\x5d\x89\x89\x4a\x95\xe2\x79\x85\xf0\xb6\xe6\x22\x90\xfb\x8d\x04\xce\x33\xd2\xd8\xe6\x08\x0b\xd5\xda\xd6\xf5\x89\x8c\x33\x5f\xf1\xa9\xc6\xc8\x19\xac\x26\x6d\xce\x54\x2d\xd7\x83\x4a\x1b\x19\xe9\x33\xce\xf7\x26\x46\xa6\x3a\x67\x19\xb7\xbd\x85\xef\x3f\x86\x73\x46\x69\x1b\xd7\x3e\x07\x6d\xe1\xe4\x40\x6b\xba\x03\xa6\x09\x9f\xe0\x05\x08\xc2\x18\x6a\xca\x71\x30\xaf\x70\x97\xdd\x39\xff\xb8\xc1\x25\x8f\x8e\x2c\x23\x74\xba\x20\x8c\xcd\x7d\x98\xe9\xd4\x7c\x1b\xbc\xd3\xa9\xb9\x0d\x54\x5a\xd6\x49\x2f\xf8\x0c\xda\x17\xf0\xc9\x7a\xb8\x68\xb7\xaf\xe1\xfc\xaa\xd3\xbe\xec\xb4\xaf\xe0\x43\x6f\x90\xa4\x4a\xb5\x0a\xe9\x17\x19\xcc\x3d\xce\xf4\x4a\x00\x09\x33\xb0\x66\x0b\x3b\x55\xad\x71\xc9\xe6\xb6\x22\xe7\x79\xac\x8b\x0c\x92\xa3\xf8\xe0\xf7\xff\x0f\x3e\xd9\xac\x2c\x9a\x41\xb8\x0b\x33\x24\x21\x66\x7a\xc8\x45\x18\xa5\x7f\xa3\xf4\xba\x51\x88\xa5\x02\x4f\x26\x7c\x44\x3b\x42\x32\x7d\x64\x9d\x6c\x96\x66\xd9\x05\x79\xb1\xa6\xa9\xf9\x52\x23\xcd\x6f\x62\x6e\x2b\xd4\x02\xb3\xbf\x86\x99\x88\x6c\xdd\x15\x89\xe3\x9f\x48\xdd\x73\xd3\x3e\x8e\x6e\xd2\x3d\x8a\x49\x06\x2b\x63\x99\x3e\x8f\x6e\xd6\x5e\x33\x4d\x80\x83\xd0\x6f\xd0\xe8\xc2\xf4\xeb\x52\x7b\x37\x4d\xe5\xf7\x1a\xcb\xaf\xd3\x32\x9e\x0a\xd2\x24\x83\x06\xf0\x56\xf3\x7b\x9c\xf5\x5e\x4b\xae\x77\x1e\xd9\x68\xa8\x77\xb7\x19\x20\x51\xa0\x46\xd7\x61\x93\x88\x32\x07\x50\xbe\x47\x41\xb9\xc6\xb8\x2f\x91\x1b\x37\x5d\x6f\xba\xe0\xdd\x54\xc6\xb4\x08\xb9\x26\x2f\x69\x33\x50\xb5\x96\xcd\x4a\x9b\xe2\xf1\x01\x53\x2e\xb6\xb5\x0e\x0c\x97\x99\x34\x6e\xed\xcf\xe1\x11\xfd\x41\xeb\x37\x37\x6b\x4a\xcf\x6c\xff\xa2\xe5\xbf\x58\xfe\x5f\x0d\x61\x4d\x83\x22\x7d\x26\xce\xe0\x39\x30\xbd\xcf\x20\xdd\xff\x62\x2c\x76\xeb\x7a\x41\x66\xd1\xe4\x9d\xd5\x5a\x96\x6a\xa9\xfe\x04\x00\x00\xff\xff\xb4\x75\x2f\xf1\xef\x04\x00\x00") +var _authorizerGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x56\xdf\x6f\xe3\xb8\x11\x7e\x16\xff\x8a\x59\x03\xe7\x4a\x0b\x59\xce\x6d\x2f\xfb\x60\xc0\x0f\xb9\xbb\xbd\xed\xb5\x49\x80\x5e\xbc\x2d\xd0\x5e\x1f\x68\x69\x14\x11\x96\x48\x2f\x49\x45\x71\x73\xfe\xdf\x8b\x21\x45\x49\x76\xdc\x5d\xa0\xd7\x00\xbb\xa6\xf8\x63\xe6\x9b\x99\xef\x1b\x72\xcf\xf3\x1d\x7f\x44\xc8\x95\x69\x94\x29\xb6\x8c\x89\x66\xaf\xb4\x85\x98\x45\xb3\x5c\x1f\xf6\x56\x2d\xab\x86\xe7\xb3\xf1\xd3\x54\xfc\xdd\xf5\x7b\x9a\x40\x99\xab\x42\xc8\xc7\xe5\x96\x1b\x7c\xff\x1d\x4d\x95\x8d\xa5\x1f\x89\x76\x59\x59\xbb\x0f\xe3\x56\xd7\x34\x34\x56\x0b\xf9\x68\xdc\xf0\x20\x9d\x51\x2b\x1a\x9c\xb1\x84\x31\x7b\xd8\x23\xdc\xb4\xb6\x52\x5a\xfc\x1b\x35\x08\x69\x51\x97\x3c\x47\x78\x61\xd1\x30\x1f\xbf\x25\xb3\xd9\x2f\xf8\xb9\x45\x63\x53\xf0\x16\xc3\x6f\x02\xa8\xb5\xd2\xec\xd8\x9b\x6b\xb8\xb1\xa8\xff\x82\x87\x89\x5d\x63\x75\x9b\x5b\x32\x3a\xac\xc2\x3f\xff\xb5\x3d\x58\xa4\x63\x65\x2b\x73\x88\x39\xbc\xbd\x70\x34\x19\xe1\xc5\x1a\x3f\xc3\x19\x14\x8d\x46\xb5\x3a\xc7\xcd\x61\x8f\xe3\xd7\xad\x90\xbb\x53\x74\xe4\xba\xe0\x16\x61\xb5\x06\x8a\x3e\xbb\x57\x5d\x9c\x64\x9f\x36\x3f\xc4\x49\xf6\x93\xd2\x0d\xb7\xf1\xec\x4e\xc9\x14\xae\xde\xc1\x9f\xb9\x84\x77\x57\x57\xef\xe1\xdb\xeb\xd5\xd5\x77\xab\xab\x6b\xf8\x78\xb7\x99\x25\x8c\x45\x15\x9d\xa7\xd2\x64\xf7\xd8\xc5\xbe\x28\x34\x4c\x81\x67\x03\xf8\x84\x45\x65\x63\xb3\x9f\xf6\x5a\x48\x5b\xc6\x55\x0a\xb3\x6f\xcc\xaf\x72\xfa\xef\x57\x39\x0b\xe9\x33\xd9\x46\xdd\xaa\x0e\x35\x85\x97\xdd\xa1\xad\x54\x91\x7c\x29\xb0\xd7\x07\x29\xb0\x84\xe0\x91\x85\x3f\x21\x2f\x50\x67\x0f\x68\xe3\x59\x48\x1d\xb7\x42\x91\xc7\x56\xd7\xd9\x5f\x5b\xd4\x87\x0f\x26\xe7\x7b\x8c\x09\xe6\x43\x0f\x73\x46\xc5\x5b\xfb\x20\xe6\x4f\xa8\xd7\xdf\x66\x57\x73\x23\x1e\xd7\xdf\x98\x59\x0a\x9e\x6c\xd9\x83\x2d\x3e\xf4\xfc\xcb\xdc\x00\x37\xea\xc1\xa1\x89\xab\xec\xa1\x6d\x62\x29\xea\x84\xfe\x5e\x63\x79\x5e\x34\x66\x41\x48\x67\x29\x38\xc0\x0e\xaf\x6d\xb5\x04\x29\xea\x81\x06\xf7\xd8\xdd\xbd\x66\x41\x3c\xd2\x26\x94\x35\x1e\x57\x53\x5f\xe3\x84\x8a\xbc\x75\x1f\x54\xa6\x0b\x90\x7f\x44\x82\xdc\x03\x9e\xd6\x4b\x94\xee\xd4\x9b\x35\x61\x21\x33\x13\x68\xce\x20\x8b\x8e\x23\xde\xf9\x05\x9e\xbe\x0c\x73\x2b\xd8\x1e\xd3\x10\x93\x53\x84\x55\x3b\x94\x17\xd5\xe0\x56\xc0\xff\xf9\xc0\x58\x84\xcf\x7b\xa1\x5d\xc5\xc0\x53\x75\x23\x1a\x64\x51\xae\x64\xd1\xef\x84\xb7\x24\xe4\xec\x07\x25\x0b\x16\xf1\xfc\x73\x2b\xe8\x24\x00\x6c\x95\xaa\x59\x54\x73\x63\x6f\xac\xc5\x66\x6f\xa7\x06\x1e\xd1\x6e\x82\x3b\x4a\x75\x9c\x40\xec\xfd\x07\x3d\x4b\xec\x3e\x8c\xce\x87\xa3\x3e\xa3\x3e\xc5\x27\x72\x3d\x8b\xeb\xff\x28\x55\x67\x79\xa8\x24\xcf\x7c\x90\xe8\xf0\xc7\x5f\xaa\x57\x5f\xaa\x88\x4f\x99\xef\x69\x48\x96\x5e\x13\x9e\xf3\xe2\x35\xdb\x9d\xfb\x84\x45\x79\xab\x35\x4a\x4b\x49\xf8\x5d\x8d\xe3\xab\xc2\xbc\x00\xf7\x2b\x12\x9a\x40\xfb\xef\x4a\xda\x9c\x16\xe8\xac\xdc\x78\xb1\xd6\x03\x4d\xfe\x67\x8e\x24\xd3\x1b\xe5\x65\x54\xcd\x19\x5d\x5e\xdc\xf7\x0a\x42\xad\x07\xab\xab\xc9\x78\xc4\xb3\x1a\x46\x29\x90\x16\x56\xe0\x44\x70\x8f\x1d\xe9\x20\x9e\xbb\xaf\xbb\xd6\xe2\xf3\xcb\x31\x39\x52\x16\x96\x4b\xf8\x88\x16\xbc\x7b\x03\xb6\x42\x68\x65\x81\xba\x3e\x90\x5a\x02\x01\x33\xda\xf7\x73\xe9\x96\xc3\x1c\x08\x03\xa5\x46\x53\xa5\x20\x15\x68\x74\x63\x9a\xdc\xa3\x2e\x95\x6e\xb0\xc8\xbe\xa0\x82\x53\xb2\x42\x3c\xe4\x7b\x68\x52\x97\x3d\xba\xa8\x09\x5a\x27\x6c\x25\x24\xd8\x4a\x18\x97\x61\xe8\x84\x2c\x54\x97\x42\xbb\x77\x77\x98\xb0\x80\xfc\x91\x02\xc9\x9c\xad\x0d\x6d\xe4\x75\xad\x3a\x03\xca\x56\xa8\xe1\x51\x69\xd5\x5a\x21\xd1\x80\x55\xb0\x43\xdc\x83\x6e\xa5\x24\xe3\xdb\x03\xb4\x86\x06\xe4\x5e\x2a\xbb\x38\xa0\x5d\x38\xd7\x58\x38\x6b\x03\xa4\x27\x5e\xb7\x08\x5d\x25\x6a\x04\x25\x71\x34\xda\xe3\x30\x27\x11\x64\xae\x45\x19\xdb\x83\x85\x35\x5c\xc3\x5b\x4f\x90\x3b\x21\x5b\x8b\x00\x40\x58\x55\x88\x82\x0e\x7b\x6e\x89\x12\xae\xa1\x11\xd2\x40\x8d\xa5\x25\xc4\x1e\x4f\xb0\xb8\xe5\xf9\x4e\x95\x25\xac\xe1\x8f\x57\xc1\xe6\x03\xba\x86\xb8\x5c\xc2\x9d\x90\xa2\x69\x1b\xe8\xb8\xf0\xfd\x0e\xb6\x68\x3b\x44\x09\xdc\x37\x41\xc3\x22\x49\xd9\xeb\xeb\xd2\x53\x0d\x8b\x53\x49\xa7\x50\xf2\xda\x60\xff\xc3\x5c\x2a\xfa\x13\x80\xcf\x79\xdd\x1a\xf1\x84\x50\xab\x7c\xc7\x22\x9e\x91\xf3\xec\x36\xbb\x55\xf9\x8e\xfa\x91\x8f\xc3\xb5\x2a\x37\x64\x2c\x2a\x7d\x27\x8b\x82\x33\x5a\x1b\x79\x9d\xfd\x6c\xfe\x81\x5a\xc5\x09\xfc\xf6\xdb\xe9\xc2\xf7\x58\x2a\x8d\xb1\x54\x5d\xc2\x22\xd7\xe8\x7a\x03\x64\x8c\xbe\xdf\x84\x76\x48\x45\x74\x93\x03\x07\xc6\x0a\x75\xa2\xae\x07\xf8\x43\xa6\x33\xb7\x7b\x72\x7e\x48\x0a\xac\xc1\xea\x16\x53\xf7\xbf\xdb\xb5\xd5\xc8\x77\x34\x3a\x32\xef\xe1\x23\x5a\x4b\x2e\x2b\xd4\x08\x0d\x72\x27\x29\x6e\x3d\x4d\xcf\x3c\xbb\x5a\x50\x06\x9c\xe8\x5c\xbd\x0b\x0f\x81\x45\xd1\x11\xb0\x36\x48\x55\x3f\x09\xfc\xa6\x28\xe2\x85\x27\x4f\x32\xcd\x82\x8f\xd1\x85\x38\x10\xc6\x10\x39\x45\x01\xdb\xd6\x5e\x56\x0e\x4e\x85\x13\x7b\x76\x25\x97\x12\x38\x9f\x03\xcf\x26\x37\xa6\x83\xd1\x13\xee\x02\x8c\x5e\xba\x5c\x9e\xe9\x8c\x50\x48\x65\x61\x30\xbc\xd4\x28\xb1\x0b\x3a\xeb\x7b\x1c\x97\x05\x48\x92\x52\xc5\x4d\x60\x27\xa9\xce\xdb\xb5\x0a\x0a\x05\x46\x4d\xa3\x20\x64\xc4\x7a\xe3\xe8\x6e\xd2\x8b\xd9\x2e\x14\x08\xfb\x7b\x4a\x7b\x89\x3c\xad\xf1\xc4\xc1\x67\x61\x5c\xdd\x7d\xea\xa7\x4d\x21\x64\x21\xb4\x03\x0f\xc2\xef\x1b\xa5\x30\x71\xd8\x57\xfe\x62\x41\x29\x7b\x79\xad\xc8\xab\x1a\x0a\xda\xc7\x6b\x54\xdf\xb3\x84\x35\xe1\xe6\xf3\x48\xbe\xe6\x90\x85\x8a\x75\x48\xf7\x87\x23\x6f\x7a\x4a\x4f\x89\xdd\x49\x6c\x56\xc1\x16\x43\xf2\x8a\x65\xcf\x5e\x16\x05\xd5\xff\x9d\x0b\x4b\x9a\x3f\x4e\xfa\xc0\x27\x59\xbb\x4e\x40\xfd\xe8\x17\xac\x91\xf7\xc9\xa3\x59\x42\x2f\xd5\xb4\x25\x73\x8d\xb0\xa5\x25\x2c\x18\x8b\x9e\xb8\x1e\x2f\x4f\xf7\xb4\x09\x85\x7b\x61\x97\xaa\x43\xdc\xd9\x52\x7b\x33\x58\x63\xee\x85\x15\x8e\x2c\xcf\x7b\x2b\x8b\xbc\xfd\x0b\x57\x76\xbf\x24\xb1\xfb\x9b\x8b\x3b\xbc\x3f\xa3\x13\x3d\xc0\x1a\xa4\xea\x58\x14\x85\x7d\xe9\xc9\xed\x4c\xc0\x29\xf3\xe1\x6e\x8e\xe9\x31\x42\xa0\x6f\xac\x6a\x44\xce\xeb\xfa\x90\x4e\x1b\xbe\xa9\xb8\x0e\xad\xe0\x0f\xc6\xe5\xde\x67\x7d\x3e\x31\x9b\x8d\xc9\x1e\x5b\x6c\x78\xf3\xad\xc7\x37\x1f\xf9\xf9\xd4\xdb\x76\x05\x9c\x9f\x60\xeb\xdf\x1d\xa1\xc6\xaf\xf8\x12\x22\x62\x4e\x38\x41\xa2\x93\x96\x44\xca\x39\x7f\x9c\x9c\xf4\xaf\x37\x27\xbd\x99\xa2\x96\xfe\x66\x0e\x21\x97\x5c\xd4\x58\x64\xf0\xa3\x30\x39\xd7\x85\x17\x94\x7b\xe4\x52\x2b\x98\x00\xec\x49\xbd\x58\x18\x4b\xe2\x73\xdd\x6d\xb1\x98\xd0\x92\x1c\xf8\x64\xd3\x1b\xcf\x33\x7b\xda\xc7\xd6\xfd\xbd\x15\xbd\x26\xe5\x38\xf7\xbd\x56\xbc\xc8\xb9\x09\xfc\x0d\x00\x86\xd7\x36\x3b\xb2\xff\x04\x00\x00\xff\xff\xf0\x7d\xaf\xa8\x9d\x10\x00\x00") func authorizerGoBytes() ([]byte, error) { return bindataRead( @@ -126,7 +126,7 @@ func collectionGo() (*asset, error) { return a, nil } -var _cosmosdbGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x56\x5f\x4f\xdc\xba\x12\x7f\x4e\x3e\xc5\x34\x12\x28\x81\x90\x85\x5e\x5d\xe9\x8a\x76\x1f\x5a\xe0\x9e\xa2\x16\x8a\x80\xaa\x0f\x55\xd5\x7a\x93\xc9\xae\xbb\x89\x9d\xda\x0e\x87\x3d\x88\xef\x7e\x34\xb6\xf3\x67\x77\xa1\xaa\xce\xd3\xd9\x97\x75\x3c\x9e\x99\xdf\xcc\xfc\x66\xec\x86\xe5\x4b\x36\x47\xc8\xa5\xae\xa5\x2e\x66\x61\xc8\xeb\x46\x2a\x03\x71\x18\x44\xb3\x95\x41\x1d\x85\x41\x94\x4b\x61\xf0\xde\xd0\xb2\xac\xed\x1f\x97\x13\x2e\x5b\xc3\x2b\xfa\x10\x68\x26\x0b\x63\x9a\x6e\x4d\x67\x1b\x25\x8d\xa4\x0d\x6d\x54\x2e\xc5\x1d\x2d\x0d\xaf\x31\x0a\xc3\x20\x9a\x73\xb3\x68\x67\x59\x2e\xeb\x49\x3b\x97\xea\x07\x9f\xcc\xe5\x24\x97\x05\xe6\x51\x98\x84\xe1\x64\x02\x1f\x1b\xc3\xa5\xd0\xa0\xb0\x51\xa8\x51\x18\x0d\x6f\xae\xce\x41\xba\xed\xd0\xac\x1a\xec\xcf\x68\xa3\xda\xdc\xc0\x43\x18\x5c\xca\xb3\x5b\x36\x87\xb5\xdf\x4c\xca\x2a\x0c\xae\x14\xde\x2a\x3e\x9f\xa3\xd2\xbd\xe4\xcb\x57\x6d\x14\x17\xf3\x30\xb8\x92\xda\x6c\x8a\x47\x52\xa6\x0c\x27\x57\xef\x71\x75\xcd\xc4\x1c\xcf\x4f\xa1\x93\x9d\x48\x61\xb8\x68\x19\x89\x3b\x4d\x2f\x7b\xb4\x81\x9c\x29\x25\xd5\x38\x0c\x26\x00\x69\xcf\xc5\xe0\xc4\x43\x04\x37\x86\x99\x56\x9f\xc8\x02\x81\x0b\x43\xf6\x0b\x5c\x33\x0b\xdf\x7f\x68\x29\x8e\x23\xca\x56\xf4\x3d\x0c\x2e\x50\x6b\x2a\xe0\xd6\x81\xda\x09\xa2\xef\x04\xa4\x6c\x45\x0e\x31\xc2\x9e\xf5\x97\x38\xb7\x71\xd2\xa9\x3c\x84\x81\x42\xd3\x2a\x01\x65\x6d\xb2\x9b\x46\x71\x61\xca\x38\xda\x29\x60\x47\x1f\xc3\x8e\x8e\x52\xc0\x6c\x80\x46\x5f\xdd\xbf\xf7\x9f\xf8\x70\xcf\xb5\x35\x3d\x0a\xc3\x19\xd6\x60\x54\x8b\xc0\x4b\x0a\x1e\xb8\x06\x59\xc2\x28\x01\x4c\x14\xc0\x8d\x86\x41\x8f\x8c\xd5\xcc\xe4\x0b\xa4\x02\xf7\xbb\x36\x90\x2d\x27\x31\x19\xb5\x59\x4d\x47\x87\x29\x83\x89\xad\x3f\x05\xe8\x5c\xa7\x20\x97\x70\x3c\xa5\x65\x16\xfb\x6c\xbc\xa2\xbd\x87\x30\xe8\x72\x40\xb2\x51\x00\xd3\xe9\x18\x40\xf0\x38\xe4\x8a\x55\x1a\x87\x32\x13\xf5\xae\xf1\x67\xcb\x15\x16\x14\xa1\x59\xa0\xc3\xe4\x53\x40\xbb\xa5\xdd\xb5\x24\x2d\x39\x56\xf6\x9c\x90\x06\x1a\xd9\xb4\x15\x33\x08\x52\x00\x23\x7b\x57\x9f\x6e\x41\x2a\x38\x3d\xfb\x70\x76\x7b\x06\xb2\x41\x65\x29\x16\xde\x31\xb5\xe5\x6b\x6a\xab\x66\x63\x29\xe3\xc8\x1a\xe7\xd4\x39\x4e\x1c\x25\x1d\xc0\x4b\x69\xce\xeb\xa6\xc2\x1a\x85\xf9\x15\x44\x06\x25\x5b\x22\x50\xaa\x2d\xad\x3d\x46\x3e\xe8\x76\x30\x36\x2c\xae\x03\xd9\xd0\xf1\x38\xae\xd1\xa8\xd5\x47\x71\xa5\x30\x97\xa2\xb0\x6d\xf5\x7f\xc6\x2b\x2c\x08\x82\xe2\xa8\xc9\x7d\xef\xb9\x04\x6e\xa0\x64\xbc\xd2\x50\xb4\x08\x46\xda\xdc\x6c\xe9\x3a\x5a\x3c\x6b\x3a\x2e\xad\xc9\x38\x71\xc1\x26\x30\xf0\x25\xa1\xc2\x97\x52\x01\x27\x56\x1c\xbe\x02\x0e\xaf\xe1\xbf\xaf\x80\xef\xef\x5b\x4a\xd0\xc1\x29\x94\x71\x12\x06\xc4\xa0\x17\x4f\x52\x2f\x05\x9a\x7e\x9e\x33\xdb\xee\xad\x8f\x8e\x5d\x61\x40\x0c\x0a\x68\x14\x66\x37\x15\x62\x13\xdb\xe5\x69\xeb\x0a\x1c\x1f\x1d\x1e\xee\xf1\x04\xf6\xc0\x6e\x5f\xf0\xaa\xe2\xda\x1a\x4c\x46\xd4\x1b\x5a\x3a\x87\xbd\x82\x19\x36\x63\x1a\x4f\x2a\x8e\x44\xf8\x42\xc6\xb9\xb9\x07\x3f\xb3\xb3\x13\xf7\x9f\x42\x8d\x66\x21\x8b\x14\x1a\x66\x16\x29\x28\xd4\xb2\x55\x39\xde\xae\x1a\x1c\xbe\x3e\x70\xb1\xf4\x43\x21\x05\xbc\x6f\x30\x37\x58\xac\xcf\xa4\x14\xb8\x48\x41\xb6\x86\x3e\x50\x95\x2c\xc7\x87\xc7\x14\x16\xc8\x0a\x9a\x9e\x36\x13\xef\xec\x87\x4f\x37\x45\x4f\x7c\x51\xa8\x1b\xd8\xb3\xf2\x6b\xd4\x8d\x14\x1a\x9d\xa0\x2f\x46\xe8\x4a\x41\x44\x58\xf9\x72\xb8\xf5\x6b\xc8\xb3\x9a\xdd\x5f\x3b\x86\xf8\x5d\x5f\x21\x32\x9b\x82\xab\x53\x9e\x7d\x73\xd1\xff\x7e\xb4\x4f\x85\xd9\x87\xd8\x87\xf5\xfb\xe5\xbf\x95\xf2\x82\x89\x15\x35\x27\x6a\xa3\x7d\xed\x67\x0a\xd9\xd2\x96\x3e\x0c\x82\x3c\xab\xe4\x3c\xfb\xcc\x94\xa0\x19\xab\xed\x8c\x65\xc6\x60\xdd\x18\xd8\x29\xfc\xbc\xdd\xc4\x6f\xd4\xca\x46\x99\x90\x85\x5a\xdb\xf5\x4b\x4a\x92\xbf\x5f\xb3\x2b\xa6\x34\x9e\x0b\x13\x53\x42\x7c\x05\xb2\x3f\xd0\xc4\xd1\xfd\x41\xad\x0f\xac\x85\x03\x56\x1a\x54\x07\xb5\x8e\x92\x14\x8e\x0e\x53\x38\xf4\x81\x59\x63\x2f\xa6\x20\x78\x35\x26\xab\xdd\xef\x60\x3f\x4b\xd9\x5a\x3f\xcb\x57\x3b\x76\x6d\xe1\xbd\xf1\xdd\xdd\x9e\x29\x23\x77\x54\x75\x3b\x96\x15\x5d\xaf\xfd\x09\x8b\xa4\xc0\x0a\x0d\xc6\x7e\x2f\x85\x65\xe2\x5b\xc8\x2a\xa5\x70\x37\xe8\x8d\x22\x77\xba\x5e\xe9\xcb\xf2\x2b\x4c\xe1\xce\xe9\x11\xa8\x21\xba\x5f\x76\xd2\xb7\x7f\x51\x2b\xc5\xeb\x9d\x93\x8e\xc6\x97\xc2\x9f\xae\x01\x8e\xa7\x4e\xe7\x12\xff\xf4\xfc\xfb\xcc\xcd\xc2\xa3\x5e\x6f\x8b\x88\x0e\xea\xe3\xc9\x24\xda\xcf\xb3\x2e\xee\x77\x52\x1b\xc1\x6a\xdc\x8f\x26\xd1\xbe\x8b\x4d\xf0\x2a\xe9\xee\xce\x71\xc5\x7c\x06\x05\xaf\xac\xeb\xbe\xd6\x5c\x8c\x4f\xcd\xda\x92\x40\xed\xda\x87\x64\xf6\xb6\x2d\x4b\x54\x0f\x8f\x7e\xac\x1e\x4f\xc1\x3e\xf9\x08\xee\x99\xa0\xa5\x8a\x67\x6d\x99\x42\x9e\xd1\x0b\xe6\x1d\x13\x45\x85\x49\xe6\x44\x31\x17\x03\x55\x9f\x62\xea\x80\xc4\x92\x43\xe1\xcf\xec\xad\x2c\x56\x30\x05\xf7\x4e\xcd\x2e\x65\x73\x52\x49\xed\x9c\x24\xfe\x88\x6f\x93\x1b\x6a\x13\x9b\x27\x61\x0e\xa8\x8c\x51\x0a\x11\x6b\x9a\x8a\xe7\x96\xe2\x13\x02\x14\x79\x46\x6f\xf3\x6e\xcc\xd7\xc1\xea\x97\xfe\x19\x9c\x9d\x30\x21\x05\xcf\x59\x75\x71\x7e\x71\xe6\xa4\xef\x71\x15\x2f\x13\xcf\x4b\xc7\xc9\x75\x38\xb6\x6b\xef\x50\x69\x2e\x05\xc1\x79\x79\x78\xf4\xbf\x83\xa3\x97\x07\xff\x39\xa2\x9b\x34\xc8\xb3\xba\xcd\xae\x3f\xc8\x7c\x19\xbb\x02\xe5\x19\x6b\xcd\x42\x2a\xfe\x17\xae\x25\x68\xbc\x9f\xbd\xe9\x96\xb1\x25\xcd\xf3\xcc\x75\xb7\x8d\x73\xf2\x49\x54\xce\x4d\x38\x1a\xb6\x54\xbd\x6c\x91\x67\xa7\x92\x4c\xfd\x2e\x47\x82\x02\x4b\x54\xdd\x6d\xdc\x8d\x6f\x5b\xa9\xec\x1a\x59\x11\x3b\xbe\x8d\x76\x6d\xcd\x28\xc4\x47\x0b\xa0\x58\xa3\xcd\x29\x3a\xda\xf4\xc7\x37\xc8\x33\xcc\x9f\xf1\x83\xee\xc5\xf4\xa9\x7e\x7c\x18\x68\xb9\x6b\x87\xbc\x25\x6a\xa7\x3e\x9e\xa7\x6b\x44\x49\xe8\x7d\xb8\xcd\x15\x3f\xbb\x32\x87\x30\xde\xb5\x93\xdb\x51\x73\xf3\x79\xb9\x89\x6f\xc8\x5c\x9f\xed\xbe\xbd\x68\x56\x0c\x93\xf4\x9f\x23\x5b\x73\x30\xa0\x94\xad\x49\xd6\x26\xa4\x3b\x20\x78\x15\x3e\x86\x7f\x07\x00\x00\xff\xff\xe2\xc7\xc9\xbf\x2b\x0e\x00\x00") +var _cosmosdbGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x56\x5f\x4f\xdc\xba\x12\x7f\x4e\x3e\xc5\x34\x12\x28\x81\x90\x85\x5e\x5d\xe9\x8a\x76\x1f\x5a\xe0\x9e\xa2\x16\x8a\x80\xa3\x3e\x54\x55\xeb\x4d\x26\xbb\xee\x26\x76\x6a\x3b\x1c\xf6\x20\xbe\xfb\xd1\xd8\xce\x9f\xdd\x85\xaa\xe7\x3c\x1d\x1e\x58\xc7\xe3\x99\xf9\xcd\xcc\x6f\xc6\x6e\x58\xbe\x64\x73\x84\x5c\xea\x5a\xea\x62\x16\x86\xbc\x6e\xa4\x32\x10\x87\x41\x34\x5b\x19\xd4\x51\x18\x44\xb9\x14\x06\xef\x0d\x2d\xcb\xda\xfe\x70\x49\xff\x05\x9a\xc9\xc2\x98\xa6\x5b\xd3\xa1\x46\x49\x63\x85\xda\xa8\x5c\x8a\x3b\x5a\x1a\x5e\x63\x14\x86\x41\x34\xe7\x66\xd1\xce\xb2\x5c\xd6\x93\x76\x2e\xd5\x77\x3e\x99\xcb\x49\x2e\x0b\xcc\xa3\x30\x09\xc3\xc9\x04\x3e\x36\x86\x4b\xa1\x41\x61\xa3\x50\xa3\x30\x1a\xde\x5c\x9d\x83\x74\xdb\xa1\x59\x35\xd8\x9f\xd1\x46\xb5\xb9\x81\x87\x30\xb8\x94\x67\xb7\x6c\x0e\x6b\x7f\x33\x29\xab\x30\xb8\x52\x78\xab\xf8\x7c\x8e\x4a\xf7\x92\xcf\x5f\xb4\x51\x5c\xcc\xc3\xe0\x4a\x6a\xb3\x29\x1e\x49\x99\x32\x9c\x5c\xbd\xc7\xd5\x35\x13\x73\x3c\x3f\x85\x4e\x76\x22\x85\xe1\xa2\x65\x24\xee\x34\xbd\xec\xd1\x06\x72\xa6\x94\x54\xe3\x30\x98\x00\xa4\x3d\x17\x83\x13\x0f\x11\xdc\x18\x66\x5a\x7d\x22\x0b\x04\x2e\x0c\xd9\x2f\x70\xcd\x2c\x7c\xfb\xae\xa5\x38\x8e\x28\x5b\xd1\xb7\x30\xb8\x40\xad\xa9\x72\x5b\x07\x6a\x27\x88\xbe\x11\x90\xb2\x15\x39\xc4\x08\x7b\xd6\x5f\xe2\xdc\xc6\x49\xa7\xf2\x10\x06\x0a\x4d\xab\x04\x94\xb5\xc9\x6e\x1a\xc5\x85\x29\xe3\x68\xa7\x80\x1d\x7d\x0c\x3b\x3a\x4a\x01\xb3\x01\x1a\x7d\x75\xbf\xde\x7f\xe2\xc3\x3d\xd7\xd6\xf4\x28\x0c\x67\x58\x83\x51\x2d\x02\x2f\x29\x78\xe0\x1a\x64\x09\xa3\x04\x30\x51\x00\x37\x1a\x06\x3d\x32\x56\x33\x93\x2f\x90\x0a\xdc\xef\xda\x40\xb6\x9c\xc4\x64\xd4\x66\x35\x1d\x1d\xa6\x0c\x26\xb6\xfe\x14\xa0\x73\x9d\x82\x5c\xc2\xf1\x94\x96\x59\xec\xb3\xf1\x8a\xf6\x1e\xc2\xa0\xcb\x01\xc9\x46\x01\x4c\xa7\x63\x00\xc1\xe3\x90\x2b\x56\x69\x1c\xca\x4c\xd4\xbb\xc6\x1f\x2d\x57\x58\x50\x84\x66\x81\x0e\x93\x4f\x01\xed\x96\x76\xd7\x92\xb4\xe4\x58\xd9\x73\x42\x1a\x68\x64\xd3\x56\xcc\x20\x48\x01\x8c\xec\x5d\xfd\x7e\x0b\x52\xc1\xe9\xd9\x87\xb3\xdb\x33\x90\x0d\x2a\x4b\xb1\xf0\x8e\xa9\x2d\x5f\x53\x5b\x35\x1b\x4b\x19\x47\xd6\x38\xa7\xce\x71\xe2\x28\xe9\x00\x5e\x4a\x73\x5e\x37\x15\xd6\x28\xcc\xcf\x20\x32\x28\xd9\x12\x81\x52\x6d\x69\xed\x31\xf2\x41\xb7\x83\xb1\x61\x71\x1d\xc8\x86\x8e\xc7\x71\x8d\x46\xad\x3e\x8a\x2b\x85\xb9\x14\x85\x6d\xab\xff\x33\x5e\x61\x41\x10\x14\x47\x4d\xee\x7b\xcf\x25\x70\x03\x25\xe3\x95\x86\xa2\x45\x30\xd2\xe6\x66\x4b\xd7\xd1\xe2\x59\xd3\x71\x69\x4d\xc6\x89\x0b\x36\x81\x81\x2f\x09\x15\xbe\x94\x0a\x38\xb1\xe2\xf0\x15\x70\x78\x0d\xff\x7d\x05\x7c\x7f\xdf\x52\x82\x0e\x4e\xa1\x8c\x93\x30\x20\x06\xbd\x78\x92\x7a\x29\xd0\xf4\xf3\x9c\xd9\x76\x6f\x7d\x74\xec\x0a\x03\x62\x50\x40\xa3\x30\xbb\xa9\x10\x9b\xd8\x2e\x4f\x5b\x57\xe0\xf8\xe8\xf0\x70\x8f\x27\xb0\x07\x76\xfb\x82\x57\x15\xd7\xd6\x60\x32\xa2\xde\xd0\xd2\x39\xec\x15\xcc\xb0\x19\xd3\x78\x52\x71\x24\xc2\x17\x32\xce\xcd\x3d\xf8\x61\x9d\x9d\xb8\xdf\x14\x6a\x34\x0b\x59\xa4\xd0\x30\xb3\x48\x41\xa1\x96\xad\xca\xf1\x76\xd5\xe0\xf0\xf5\x81\x8b\xa5\x1f\x0a\x29\xe0\x7d\x83\xb9\xc1\x62\x7d\x26\xa5\xc0\x45\x0a\xb2\x35\xf4\x81\xaa\x64\x39\x3e\x3c\xa6\xb0\x40\x56\xd0\xf4\xb4\x99\x78\x67\x3f\x7c\xba\x29\x7a\xe2\x8b\x42\xdd\xc0\x9e\x95\x5f\xa3\x6e\xa4\xd0\xe8\x04\x7d\x31\x42\x57\x0a\x22\xc2\xca\x97\xc3\xad\x5f\x43\x9e\xd5\xec\xfe\xda\x31\xc4\xef\xfa\x0a\x91\xd9\x14\x5c\x9d\xf2\xec\xab\x8b\xfe\xd7\xa3\x7d\x2a\xcc\x3e\xc4\x3e\xac\x5f\x2f\xff\xad\x94\x17\x4c\xac\xa8\x39\x51\x1b\xed\x6b\x3f\x53\xc8\x96\xb6\xf4\x61\x10\xe4\x59\x25\xe7\xd9\x27\xa6\x04\xcd\x58\x6d\x67\x2c\x33\x06\xeb\xc6\xc0\x4e\xe1\xe7\xed\x26\x7e\xa3\x56\x36\xca\x84\x2c\xd4\xda\xae\x5f\x52\x92\xfc\xfd\x9a\x5d\x31\xa5\xf1\x5c\x98\x98\x12\xe2\x2b\x90\xfd\x86\x26\x8e\xee\x0f\x6a\x7d\x60\x2d\x1c\xb0\xd2\xa0\x3a\xa8\x75\x94\xa4\x70\x74\x98\xc2\xa1\x0f\xcc\x1a\x7b\x31\x05\xc1\xab\x31\x59\xed\x7e\x07\xfb\x59\xca\xd6\xfa\x59\xbe\xda\xb1\x6b\x0b\xef\x8d\xef\xee\xf6\x4c\x19\xb9\xa3\xaa\xdb\xb1\xac\xe8\x7a\xed\x4f\x58\x24\x05\x56\x68\x30\xf6\x7b\x29\x2c\x13\xdf\x42\x56\x29\x85\xbb\x41\x6f\x14\xb9\xd3\xf5\x4a\x9f\x97\x5f\x60\x0a\x77\x4e\x8f\x40\x0d\xd1\xfd\xb4\x93\xbe\xfe\x8b\x5a\x29\x5e\xef\x9c\x74\x34\xbe\x14\xfe\x70\x0d\x70\x3c\x75\x3a\x97\xf8\x87\xe7\xdf\x27\x6e\x16\x1e\xf5\x7a\x5b\x44\x74\x50\x1f\x4f\x26\xd1\x7e\x9e\x75\x71\xbf\x93\xda\x08\x56\xe3\x7e\x34\x89\xf6\x5d\x6c\x82\x57\x49\x77\x77\x8e\x2b\xe6\x33\x28\x78\x65\x5d\xf7\xb5\xe6\x62\x7c\x6a\xd6\x96\x04\x6a\xd7\xbe\x20\xb3\xb7\x6d\x59\xa2\x7a\x78\xf4\x63\xf5\x78\x0a\xf6\xc9\x47\x70\xcf\x04\x2d\x55\x3c\x6b\xcb\x14\xf2\x8c\x5e\x30\xef\x98\x28\x2a\x4c\x32\x27\x8a\xb9\x18\xa8\xfa\x14\x53\x07\x24\x96\x1c\x0a\x7f\x64\x6f\x65\xb1\x82\x29\x70\x99\x5d\xca\xe6\xa4\x92\xda\x39\x48\xbc\xd8\xb7\xc8\x0d\xb5\x88\xcd\x91\x30\x07\x54\xc2\x28\x85\x88\x35\x4d\xc5\x73\x4b\xef\x09\x81\x89\x3c\x9b\xb7\x39\x37\xe6\xea\x60\xf5\x73\xff\x04\xce\x4e\x98\x90\x82\xe7\xac\xba\x38\xbf\x38\x73\xd2\xf7\xb8\x8a\x97\x89\xe7\xa4\xe3\xe3\x3a\x1c\xdb\xb1\x77\xa8\x34\x97\x82\xe0\xbc\x3c\x3c\xfa\xdf\xc1\xd1\xcb\x83\xff\x1c\xd1\x2d\x4a\x59\xc8\x33\xd6\x9a\x85\x54\xfc\x4f\x5c\x4b\x47\x97\xd8\x91\x38\x7b\xd3\x2d\x63\xcb\x94\xe7\xe9\xfa\x77\x32\x6c\xef\xa2\x6e\xf2\x5a\x8f\x8b\x3c\x3b\x95\xe4\xe2\x57\x09\x13\x14\x58\xa2\xea\xae\xe6\x6e\x96\xdb\xb2\x65\xd7\xc8\x8a\xd8\x91\x6f\xb4\x6b\x8b\x48\x77\xf1\x63\x4c\x79\x28\xd6\x38\x74\x8a\x8e\x43\xfd\xf1\x0d\x26\x0d\xc3\x68\xfc\xba\x7b\x31\x7d\xaa\x39\x47\xa9\xdc\xb5\x13\xdf\xb2\xb6\x53\x1f\x0f\xd7\x35\xe6\x24\xf4\x58\xdc\x26\x8f\x1f\x64\x99\x43\x18\xef\xda\x31\xee\x78\xba\xf9\xd6\xdc\xc4\x37\x64\xae\xcf\x76\xdf\x6b\x34\x38\x86\xb1\xfa\xcf\x91\xad\x39\x18\x50\xca\xd6\x24\x6b\xe3\xd2\x1d\x10\xbc\x0a\x1f\xc3\xbf\x02\x00\x00\xff\xff\x6b\x72\x70\xbe\x31\x0e\x00\x00") func cosmosdbGoBytes() ([]byte, error) { return bindataRead( diff --git a/vendor/modules.txt b/vendor/modules.txt index d9f0fb44cb1..f28d8b79bfc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -736,7 +736,7 @@ github.com/itchyny/gojq/cli # github.com/itchyny/timefmt-go v0.1.5 ## explicit; go 1.17 github.com/itchyny/timefmt-go -# github.com/jewzaam/go-cosmosdb v0.0.0-20240320220716-88298caebe4a +# github.com/jewzaam/go-cosmosdb v0.0.0-20240603205015-e096456eff37 ## explicit; go 1.18 github.com/jewzaam/go-cosmosdb/cmd/gencosmosdb github.com/jewzaam/go-cosmosdb/pkg/gencosmosdb