예제 #1
0
    public void ParseResourceGroupId(string resourceId)
    {
        var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

        Assert.Equal(SUBSCRIPTION_ID, resourceIdentifier.SubscriptionId);
        Assert.Equal("TestRG", resourceIdentifier.ResourceGroup);
    }
    protected override async Task <Component> CreateComponentAsync(Component component, Organization componentOrganization, DeploymentScope componentDeploymentScope, Project componentProject, User contextUser, IAsyncCollector <ICommand> commandQueue)
    {
        if (component is null)
        {
            throw new ArgumentNullException(nameof(component));
        }

        if (!AzureResourceIdentifier.TryParse(component.ResourceId, out var resourceId))
        {
            component.ResourceId = await CreateResourceIdAsync(component)
                                   .ConfigureAwait(false);

            resourceId = AzureResourceIdentifier.Parse(component.ResourceId);

            var sessionIdenity = await azureResourceService.AzureSessionService
                                 .GetIdentityAsync()
                                 .ConfigureAwait(false);

            component.ResourceUrl = resourceId.GetPortalUrl(sessionIdenity.TenantId);

            component = await componentRepository
                        .SetAsync(component)
                        .ConfigureAwait(false);
        }

        return(await UpdateComponentAsync(component, contextUser, commandQueue).ConfigureAwait(false));
    }
예제 #3
0
        public async Task <string> RunActivity(
            [ActivityTrigger] IDurableActivityContext activityContext,
            ILogger log)
        {
            if (activityContext is null)
            {
                throw new ArgumentNullException(nameof(activityContext));
            }

            var resourceId = activityContext.GetInput <string>();

            try
            {
                var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

                if (string.IsNullOrEmpty(resourceIdentifier.ResourceGroup))
                {
                    throw new RetryCanceledException($"Resource id must include resource group information: {resourceId}");
                }
                else if (resourceIdentifier.ResourceTypes?.Any() ?? false)
                {
                    throw new RetryCanceledException($"Resource id must not include resource type information: {resourceId}");
                }
                else
                {
                    var resourceGroup = await azureResourceService
                                        .GetResourceGroupAsync(resourceIdentifier.SubscriptionId, resourceIdentifier.ResourceGroup)
                                        .ConfigureAwait(false);

                    if (resourceGroup is null)
                    {
                        return(default);
        public void ParseResourceGroupId(string resourceId)
        {
            var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

            Assert.Equal(Guid.Empty, resourceIdentifier.SubscriptionId);
            Assert.Equal("TestRG", resourceIdentifier.ResourceGroup);
        }
    public async Task <Component> Run(
        [ActivityTrigger] IDurableActivityContext context,
        ILogger log)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (log is null)
        {
            throw new ArgumentNullException(nameof(log));
        }

        var component = context.GetInput <Input>().Component;

        if (!AzureResourceIdentifier.TryParse(component.IdentityId, out var identityId))
        {
            var deploymentScope = await deploymentScopeRepository
                                  .GetAsync(component.Organization, component.DeploymentScopeId)
                                  .ConfigureAwait(false);

            var project = await projectRepository
                          .GetAsync(component.Organization, component.ProjectId)
                          .ConfigureAwait(false);

            var projectResourceId = AzureResourceIdentifier.Parse(project.ResourceId);

            var session = await azureResourceService.AzureSessionService
                          .CreateSessionAsync(projectResourceId.SubscriptionId)
                          .ConfigureAwait(false);

            var identities = await session.Identities
                             .ListByResourceGroupAsync(projectResourceId.ResourceGroup, loadAllPages : true)
                             .ConfigureAwait(false);

            var identity = identities
                           .SingleOrDefault(i => i.Name.Equals(deploymentScope.Id, StringComparison.OrdinalIgnoreCase));

            if (identity is null)
            {
                var location = await GetLocationAsync(component)
                               .ConfigureAwait(false);

                identity = await session.Identities
                           .Define(deploymentScope.Id)
                           .WithRegion(location)
                           .WithExistingResourceGroup(projectResourceId.ResourceGroup)
                           .CreateAsync()
                           .ConfigureAwait(false);
            }

            component.IdentityId = identity.Id;
        }


        return(component);
    }
예제 #6
0
    protected AzureResource(string resourceId, IAzureResourceService azureResourceService)
    {
        if (resourceId is null)
        {
            throw new ArgumentNullException(nameof(resourceId));
        }

        ResourceId           = AzureResourceIdentifier.Parse(resourceId);
        AzureResourceService = azureResourceService ?? throw new ArgumentNullException(nameof(azureResourceService));
    }
예제 #7
0
    internal AzureResource(string resourceId)
    {
        if (resourceId is null)
        {
            throw new ArgumentNullException(nameof(resourceId));
        }

        ResourceId           = AzureResourceIdentifier.Parse(resourceId);
        AzureResourceService = null;
    }
예제 #8
0
    public void ParseManagementGroupId(string resourceId)
    {
        var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

        Assert.Equal(Guid.Empty, resourceIdentifier.SubscriptionId);
        Assert.Null(resourceIdentifier.ResourceGroup);

        Assert.Equal("managementGroups", resourceIdentifier.ResourceTypes[0].Key);
        Assert.Equal("11111111-1111-1111-1111-111111111111", resourceIdentifier.ResourceTypes[0].Value);
    }
예제 #9
0
    public void ParseResourceId(string resourceId)
    {
        var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

        Assert.Equal(SUBSCRIPTION_ID, resourceIdentifier.SubscriptionId);
        Assert.Equal("TestRG", resourceIdentifier.ResourceGroup);
        Assert.True(resourceIdentifier.ResourceTypes.Count == 2);

        Assert.Equal("resourceProviders", resourceIdentifier.ResourceTypes[0].Key);
        Assert.Equal("TestProviderName", resourceIdentifier.ResourceTypes[0].Value);

        Assert.Equal("TestResourceType", resourceIdentifier.ResourceTypes[1].Key);
        Assert.Equal("TestResourceName", resourceIdentifier.ResourceTypes[1].Value);
    }
        public void ParseResourceIdUnnamed(string resourceId)
        {
            var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId, allowUnnamedResource: true);

            Assert.Equal(Guid.Empty, resourceIdentifier.SubscriptionId);
            Assert.Equal("TestRG", resourceIdentifier.ResourceGroup);
            Assert.True(resourceIdentifier.ResourceTypes.Count == 2);
            Assert.Null(resourceIdentifier.ResourceName);

            Assert.Equal("resourceProviders", resourceIdentifier.ResourceTypes[0].Key);
            Assert.Equal("TestProviderName", resourceIdentifier.ResourceTypes[0].Value);

            Assert.Equal("TestResourceType", resourceIdentifier.ResourceTypes[1].Key);
            Assert.Null(resourceIdentifier.ResourceTypes[1].Value);
        }
        public void ParseResourceId(string resourceId)
        {
            var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

            Assert.Equal(Guid.Empty, resourceIdentifier.SubscriptionId);
            Assert.Equal("TestRG", resourceIdentifier.ResourceGroup);

            Assert.Equal("resourceProviders", resourceIdentifier.ResourceTypes[0].Key);
            Assert.Equal("TestProviderName", resourceIdentifier.ResourceTypes[0].Value);

            Assert.Equal("TestResourceType", resourceIdentifier.ResourceTypes[1].Key);
            Assert.Equal("TestResourceName", resourceIdentifier.ResourceTypes[1].Value);

            if (resourceIdentifier.ResourceTypes.Count == 3)
            {
                Assert.Equal("SubResourceType", resourceIdentifier.ResourceTypes[2].Key);
                Assert.Equal("SubResourceName", resourceIdentifier.ResourceTypes[2].Value);
            }
        }
예제 #12
0
        public async Task RunActivity(
            [ActivityTrigger] IDurableActivityContext functionContext,
            ILogger log)
        {
            if (functionContext is null)
            {
                throw new ArgumentNullException(nameof(functionContext));
            }

            var resourceId = functionContext.GetInput <string>();

            try
            {
                var resourceIdentifier = AzureResourceIdentifier.Parse(resourceId);

                if (string.IsNullOrEmpty(resourceIdentifier.ResourceGroup))
                {
                    throw new RetryCanceledException($"Resource id must include resource group information: {resourceId}");
                }
                else if (resourceIdentifier.ResourceTypes?.Any() ?? false)
                {
                    throw new RetryCanceledException($"Resource id must not include resource type information: {resourceId}");
                }
                else
                {
                    var resourceGroup = await azureResourceService
                                        .GetResourceGroupAsync(resourceIdentifier.SubscriptionId, resourceIdentifier.ResourceGroup)
                                        .ConfigureAwait(false);

                    await(resourceGroup?.DeleteAsync(true) ?? Task.CompletedTask)
                    .ConfigureAwait(false);
                }
            }
            catch (Exception exc)
            {
                log.LogError(exc, $"Activity '{nameof(ResourceGroupDeleteActivity)} failed: {exc.Message}");

                throw exc.AsSerializable();
            }
        }
 public void ParseResourceIdUnnamedNotAllowed(string resourceId)
 {
     Assert.Throws <ArgumentException>(() => AzureResourceIdentifier.Parse(resourceId, allowUnnamedResource: false));
 }
    public async Task <ComponentTask> Run(
        [ActivityTrigger] IDurableActivityContext context,
        ILogger log)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (log is null)
        {
            throw new ArgumentNullException(nameof(log));
        }

        var componentTask = context.GetInput <Input>().ComponentTask;

        var component = await componentRepository
                        .GetAsync(componentTask.ProjectId, componentTask.ComponentId)
                        .ConfigureAwait(false);

        var componentTemplate = await componentTemplateRepository
                                .GetAsync(component.Organization, component.ProjectId, component.TemplateId)
                                .ConfigureAwait(false);

        if (string.IsNullOrEmpty(componentTemplate.TaskRunner?.Id))
        {
            componentTask.Started   = componentTask.Created;
            componentTask.Finished  = componentTask.Created;
            componentTask.ExitCode  = 0;
            componentTask.TaskState = TaskState.Succeeded;
        }
        else
        {
            var pairedRegionFallback = false; pairedRegionFallback:

            try
            {
                var tenantId = await azureSessionService
                               .GetTenantIdAsync()
                               .ConfigureAwait(false);

                var organization = await organizationRepository
                                   .GetAsync(tenantId.ToString(), componentTask.Organization)
                                   .ConfigureAwait(false);

                var project = await projectRepository
                              .GetAsync(componentTask.Organization, componentTask.ProjectId)
                              .ConfigureAwait(false);

                var projectResourceId = AzureResourceIdentifier
                                        .Parse(project.ResourceId);

                // we must not use the component task's id as runner label as this
                // will violate the maximum length of a SSL certificats CN name.

                var componentRunnerCpu      = 1;
                var componentRunnerMemory   = 2;
                var componentRunnerLocation = await GetLocationAsync(component, projectResourceId.SubscriptionId, componentRunnerCpu, componentRunnerMemory, pairedRegionFallback).ConfigureAwait(false);

                var componentRunnerRoot  = $".{componentRunnerLocation}.azurecontainer.io";
                var componentRunnerLabel = CreateUniqueString(64 - componentRunnerRoot.Length, componentTask.Id);
                var componentRunnerHost  = string.Concat(componentRunnerLabel, componentRunnerRoot);

                var componentRunnerDefinition = new
                {
                    location = componentRunnerLocation,
                    identity = new
                    {
                        type = "UserAssigned",
                        userAssignedIdentities = new Dictionary <string, object>()
                        {
                            { component.IdentityId, new { } }
                        }
                    },
                    properties = new
                    {
                        imageRegistryCredentials = await GetRegistryCredentialsAsync(organization).ConfigureAwait(false),
                        containers    = await GetContainersAsync(organization, component, componentTemplate, componentTask, componentRunnerHost, componentRunnerCpu, componentRunnerMemory).ConfigureAwait(false),
                        osType        = "Linux",
                        restartPolicy = "Never",
                        ipAddress     = GetIpAddress(componentTemplate, componentRunnerLabel),
                        volumes       = await GetVolumesAsync(project, component, componentTemplate).ConfigureAwait(false)
                    }
                };

                var token = await azureSessionService
                            .AcquireTokenAsync()
                            .ConfigureAwait(false);

                try
                {
                    var response = await projectResourceId.GetApiUrl(azureSessionService.Environment)
                                   .AppendPathSegment($"/providers/Microsoft.ContainerInstance/containerGroups/{component.Id}")
                                   .SetQueryParam("api-version", "2021-09-01")
                                   .WithOAuthBearerToken(token)
                                   .PutJsonAsync(componentRunnerDefinition)
                                   .ConfigureAwait(false);

                    var responseJson = await response
                                       .GetJsonAsync <JObject>()
                                       .ConfigureAwait(false);

                    componentTask.ResourceId = responseJson.SelectToken("$.id").ToString();
                }
                catch (FlurlHttpException apiExc) when(apiExc.StatusCode == StatusCodes.Status409Conflict)
                {
                    if (pairedRegionFallback)
                    {
                        // give azure some time to do its work
                        // and remove any naming conflicts

                        await Task
                        .Delay(TimeSpan.FromMinutes(1))
                        .ConfigureAwait(false);
                    }

                    var response = await projectResourceId.GetApiUrl(azureSessionService.Environment)
                                   .AppendPathSegment($"/providers/Microsoft.ContainerInstance/containerGroups/{componentTask.Id}")
                                   .SetQueryParam("api-version", "2019-12-01")
                                   .AllowHttpStatus(HttpStatusCode.NotFound)
                                   .WithOAuthBearerToken(token)
                                   .GetAsync()
                                   .ConfigureAwait(false);

                    if (response.IsSuccessStatusCode())
                    {
                        var responseJson = await response
                                           .GetJsonAsync <JObject>()
                                           .ConfigureAwait(false);

                        componentTask.ResourceId = responseJson
                                                   .SelectToken("$.id")?
                                                   .ToString();
                    }
                    else
                    {
                        throw;
                    }
                }
                catch (FlurlHttpException apiExc) when(apiExc.StatusCode == StatusCodes.Status504GatewayTimeout && !pairedRegionFallback)
                {
                    // enable paired region fallback - this will affect error handling
                    pairedRegionFallback = true;

                    // goto paired region fallback label and restart processing
                    goto pairedRegionFallback;
                }

                componentTask = await componentTaskRepository
                                .SetAsync(componentTask)
                                .ConfigureAwait(false);
            }
            catch (Exception exc)
            {
                var errorMessage = exc.Message;

                if (exc is FlurlHttpException flurlExc && flurlExc.Call.Completed)
                {
                    var error = await flurlExc
                                .GetResponseJsonAsync <JObject>()
                                .ConfigureAwait(false);

                    errorMessage = error.SelectToken("..message")?.ToString() ?? errorMessage;
                }

                log.LogError(exc, $"Failed to create runner for component deployment {componentTask}: {errorMessage}");

                throw exc.AsSerializable();
            }
        }

        return(componentTask);
    }