예제 #1
0
        public static async Task Run(
            [TimerTrigger("0 0 0/1 * * *")] TimerInfo timerInfo,
            [CosmosDB(
                 databaseName: Webhook.DatabaseName,
                 collectionName: Webhook.CollectionName,
                 ConnectionStringSetting = "CosmosDBConnectionString")] DocumentClient documentClient,
            ILogger log,
            ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                         .SetBasePath(context.FunctionAppDirectory)
                         .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                         .AddEnvironmentVariables()
                         .Build();

            var dimensionConfigs = JsonConvert.DeserializeObject <DimensionConfig[]>(config["DIMENSION_CONFIG"]);

            log.LogTrace($"Dimension configs: {JsonConvert.SerializeObject(dimensionConfigs)}");

            using (var httpClient = HttpClientFactory.Create())
            {
                var token = await CronJob.GetToken(httpClient, config, log).ConfigureAwait(continueOnCapturedContext: false);

                httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
                string sqlExpression = "Select * from c where c.processStatus=false";
                using (var queryable = documentClient.CreateDocumentQuery <BillingEntry>(UriFactory.CreateDocumentCollectionUri(Webhook.DatabaseName, Webhook.CollectionName), sqlExpression).AsDocumentQuery())
                {
                    while (queryable.HasMoreResults)
                    {
                        foreach (var billingEntry in await queryable.ExecuteNextAsync <BillingEntry>().ConfigureAwait(continueOnCapturedContext: false))
                        {
                            foreach (var dimensionConfig in dimensionConfigs)
                            {
                                var response = await CronJob.EmitUsageEvents(config, httpClient, dimensionConfig, billingEntry).ConfigureAwait(continueOnCapturedContext: false);

                                var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false);

                                if (response.IsSuccessStatusCode)
                                {
                                    log.LogTrace($"Successfully emitted a usage event. Reponse body: {responseBody}");

                                    // update cosmosdb document
                                    billingEntry.processStatus = true;
                                    await documentClient.UpsertDocumentAsync(UriFactory.CreateDocumentCollectionUri(Webhook.DatabaseName, Webhook.CollectionName), billingEntry).ConfigureAwait(continueOnCapturedContext: false);
                                }
                                else
                                {
                                    log.LogError($"Failed to emit a usage event. Error code: {response.StatusCode}. Failure cause: {response.ReasonPhrase}. Response body: {responseBody}");
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #2
0
        public static async Task Run(
            [TimerTrigger("0 0 0/1 * * *")] TimerInfo timerInfo,
            ILogger log,
            ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                         .SetBasePath(context.FunctionAppDirectory)
                         .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                         .AddEnvironmentVariables()
                         .Build();

            var dimensionConfigs = JsonConvert.DeserializeObject <DimensionConfig[]>(config["DIMENSION_CONFIG"]);

            log.LogTrace($"Dimension configs: {JsonConvert.SerializeObject(dimensionConfigs)}");

            using (var armHttpClient = HttpClientFactory.Create())
            {
                var armToken = await CronJob.GetToken(config, armHttpClient, log, "https://management.core.windows.net/").ConfigureAwait(continueOnCapturedContext: false);

                armHttpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {armToken}");

                var applicationResourceId = await CronJob.GetResourceGroupManagedBy(config, armHttpClient, log).ConfigureAwait(continueOnCapturedContext: false);

                var application = await CronJob.GetApplication(applicationResourceId, config, armHttpClient, log).ConfigureAwait(continueOnCapturedContext: false);

                if (application != null)
                {
                    log.LogInformation($"Authorization bearer token: {armToken}");
                    log.LogInformation($"Resource usage id: {application.Properties.BillingDetails?.ResourceUsageId}");
                    log.LogInformation($"Plan name: {application.Plan.Name}");

                    foreach (var dimensionConfig in dimensionConfigs)
                    {
                        var response = await CronJob.EmitUsageEvents(config, armHttpClient, dimensionConfig, application.Properties.BillingDetails?.ResourceUsageId, application.Plan.Name).ConfigureAwait(continueOnCapturedContext: false);

                        var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false);

                        if (response.IsSuccessStatusCode)
                        {
                            log.LogTrace($"Successfully emitted a usage event. Reponse body: {responseBody}");
                        }
                        else
                        {
                            log.LogError($"Failed to emit a usage event. Error code: {response.StatusCode}. Failure cause: {response.ReasonPhrase}. Response body: {responseBody}");
                        }
                    }
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Emits the usage event to the configured MARKETPLACEAPI_URI.
        /// </summary>
        private static async Task <HttpResponseMessage> EmitUsageEvents(IConfigurationRoot config, HttpClient httpClient, DimensionConfig dimensionConfig, string resourceUsageId, string planId)
        {
            var usageEvent = new UsageEventDefinition
            {
                ResourceId         = resourceUsageId,
                Quantity           = dimensionConfig.Quantity,
                Dimension          = dimensionConfig.Dimension,
                EffectiveStartTime = DateTime.UtcNow,
                PlanId             = planId
            };

            if (CronJob.IsLocalRun(config))
            {
                return(new HttpResponseMessage
                {
                    Content = new StringContent(JsonConvert.SerializeObject(usageEvent), UnicodeEncoding.UTF8, "application/json"),
                    StatusCode = HttpStatusCode.OK
                });
            }
            return(await httpClient.PostAsJsonAsync(config["MARKETPLACEAPI_URI"], usageEvent).ConfigureAwait(continueOnCapturedContext: false));
        }
예제 #4
0
        /// <summary>
        /// Gets the token for the attached user-assigned managed identity.
        /// </summary>
        public static async Task <string> GetToken(HttpClient httpClient, IConfigurationRoot config, ILogger log)
        {
            if (CronJob.IsLocalRun(config))
            {
                return("token");
            }

            // TOKEN_RESOURCE and MSI_CLIENT_ID come from the configs
            using (var request = new HttpRequestMessage(HttpMethod.Get, $"{config["MSI_ENDPOINT"]}/?resource={config["TOKEN_RESOURCE"]}&clientId={config["MSI_CLIENT_ID"]}&api-version=2017-09-01"))
            {
                request.Headers.Add("Secret", config["MSI_SECRET"]);
                var response = await httpClient.SendAsync(request).ConfigureAwait(continueOnCapturedContext: false);

                if (response?.IsSuccessStatusCode != true)
                {
                    log.LogError("Failed to get token for user-assigned MSI. Please check that all the config flags are set properly and the MSI is attached.");
                }
                var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false);

                return(JsonConvert.DeserializeObject <TokenDefinition>(responseBody).Access_token);
            }
        }
예제 #5
0
        /// <summary>
        /// Gets the token for the system-assigned managed identity.
        /// </summary>
        private static async Task <string> GetToken(IConfigurationRoot config, HttpClient httpClient, ILogger log, string resource)
        {
            if (CronJob.IsLocalRun(config))
            {
                return("token");
            }

            // TOKEN_RESOURCE come from the configs
            using (var request = new HttpRequestMessage(HttpMethod.Get, $"{config["MSI_ENDPOINT"]}/?resource={resource}&api-version=2017-09-01"))
            {
                request.Headers.Add("Secret", config["MSI_SECRET"]);
                var response = await httpClient.SendAsync(request).ConfigureAwait(continueOnCapturedContext: false);

                if (response?.IsSuccessStatusCode != true)
                {
                    log.LogError($"Failed to get token for system-assigned MSI. Please check that the MSI is set up properly. Error: {response.Content.ReadAsStringAsync().Result}");
                }
                var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false);

                return(JsonConvert.DeserializeObject <TokenDefinition>(responseBody).Access_token);
            }
        }