Example #1
0
        public static DeploymentOrchestrationInput Validate(DeploymentOrchestrationInput input, ARMFunctions functions, IInfrastructure infrastructure)
        {
            if (input.Template != null)
            {
                return(input);
            }
            input.Template = Template.Parse(input.TemplateContent, input, functions, infrastructure);
            foreach (var res in input.Template.Resources)
            {
                #region Deployment
                if (res.FullType == infrastructure.BuiltinServiceTypes.Deployments)
                {
                    var deploy = Parse(res, input, functions, infrastructure);
                    input.Deployments.Add(deploy.DeploymentName, deploy);
                    foreach (var item in deploy.Deployments.Values)
                    {
                        input.Deployments.Add(item.DeploymentName, item);
                    }
                    deploy.Deployments.Clear();
                }
                #endregion

                #region dependsOn
                for (int i = res.DependsOn.Count - 1; i >= 0; i--)
                {
                    string dependsOnName = res.DependsOn[i];
                    // https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/define-resource-dependency#dependson
                    // When a conditional resource isn't deployed, Azure Resource Manager automatically removes it from the required dependencies.
                    if (!input.Template.Resources.ContainsKey(dependsOnName))
                    {
                        if (input.Template.ConditionFalseResources.Contains(dependsOnName))
                        {
                            res.DependsOn.RemoveAt(i);
                        }
                        else
                        {
                            throw new Exception($"cannot find dependson resource named '{dependsOnName}'");
                        }
                    }
                    // check duplicated dependsOn
                    if (HasSameName(res.DependsOn, i - 1, dependsOnName))
                    {
                        res.DependsOn.RemoveAt(i);
                    }
                }
                // TODO: check circular dependencies
                #endregion
            }
            return(input);
        }
Example #2
0
        public static DeploymentOrchestrationInput Parse(Resource resource,
                                                         DeploymentContext deploymentContext,
                                                         ARMFunctions functions,
                                                         IInfrastructure infrastructure)
        {
            var armContext = new Dictionary <string, object>()
            {
                { ContextKeys.ARM_CONTEXT, deploymentContext },
                { ContextKeys.IS_PREPARE, true }
            };

            using var doc = JsonDocument.Parse(resource.Properties);
            var rootElement = doc.RootElement;

            var mode = DeploymentMode.Incremental;

            if (rootElement.TryGetProperty("mode", out JsonElement _mode))
            {
                if (_mode.GetString().Equals(DeploymentMode.Complete.ToString(), StringComparison.OrdinalIgnoreCase))
                {
                    mode = DeploymentMode.Complete;
                }
                if (_mode.GetString().Equals(DeploymentMode.OnlyCreation.ToString(), StringComparison.OrdinalIgnoreCase))
                {
                    mode = DeploymentMode.OnlyCreation;
                }
            }
            string template = string.Empty;

            if (rootElement.TryGetProperty("template", out JsonElement _template))
            {
                template = _template.GetRawText();
            }
            TemplateLink templateLink = null;

            if (rootElement.TryGetProperty("templateLink", out JsonElement _templateLink))
            {
                templateLink = new TemplateLink()
                {
                    ContentVersion = _templateLink.GetProperty("contentVersion").GetString(),
                    Uri            = functions.Evaluate(_templateLink.GetProperty("uri").GetString(), armContext).ToString()
                };
            }
            // https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#scope-for-expressions-in-nested-templates
            string         parameters     = string.Empty;
            ParametersLink parametersLink = null;

            if (rootElement.TryGetProperty("expressionEvaluationOptions", out JsonElement _expressionEvaluationOptions) &&
                _expressionEvaluationOptions.GetProperty("scope").GetString().Equals("inner", StringComparison.OrdinalIgnoreCase))
            {
                if (rootElement.TryGetProperty("parameters", out JsonElement _parameters))
                {
                    parameters = _parameters.GetRawText();
                }
                if (rootElement.TryGetProperty("parametersLink", out JsonElement _parametersLink))
                {
                    parametersLink = new ParametersLink()
                    {
                        ContentVersion = _parametersLink.GetProperty("contentVersion").GetString(),
                        Uri            = functions.Evaluate(_parametersLink.GetProperty("uri").GetString(), armContext).ToString()
                    };
                }
            }
            else
            {
                parameters                  = deploymentContext.Parameters;
                using MemoryStream ms       = new MemoryStream();
                using Utf8JsonWriter writer = new Utf8JsonWriter(ms);
                writer.WriteStartObject();
                using var doc1 = JsonDocument.Parse(template);
                var root1 = doc1.RootElement;
                foreach (var node in root1.EnumerateObject())
                {
                    if (node.Name.Equals("parameters", StringComparison.OrdinalIgnoreCase) ||
                        node.Name.Equals("variables", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }
                    writer.WritePropertyName(node.Name);
                    node.Value.WriteTo(writer);
                }
                if (!string.IsNullOrWhiteSpace(deploymentContext.Template.Parameters))
                {
                    using var p = JsonDocument.Parse(deploymentContext.Template.Parameters);
                    writer.WritePropertyName("parameters");
                    p.RootElement.WriteTo(writer);
                }
                if (!string.IsNullOrWhiteSpace(deploymentContext.Template.Variables))
                {
                    using var v = JsonDocument.Parse(deploymentContext.Template.Variables);
                    writer.WritePropertyName("variables");
                    v.RootElement.WriteTo(writer);
                }

                writer.WriteEndObject();
                writer.Flush();
                template = Encoding.UTF8.GetString(ms.ToArray());
            }
            var(groupId, groupType, hierarchyId) = infrastructure.GetGroupInfo(resource.ManagementGroupId, resource.SubscriptionId, resource.ResourceGroup);
            var deployInput = new DeploymentOrchestrationInput()
            {
                RootId            = deploymentContext.RootId,
                DeploymentId      = Guid.NewGuid().ToString("N"),
                ParentId          = deploymentContext.GetResourceId(infrastructure),
                GroupId           = groupId,
                GroupType         = groupType,
                HierarchyId       = hierarchyId,
                CorrelationId     = deploymentContext.CorrelationId,
                SubscriptionId    = resource.SubscriptionId,
                ManagementGroupId = resource.ManagementGroupId,
                ResourceGroup     = resource.ResourceGroup,
                DeploymentName    = resource.Name,
                Mode            = mode,
                TemplateContent = template,
                TemplateLink    = templateLink,
                Parameters      = parameters,
                ParametersLink  = parametersLink,
                ApiVersion      = resource.ApiVersion,
                CreateByUserId  = deploymentContext.CreateByUserId,
                LastRunUserId   = deploymentContext.LastRunUserId,
                DependsOn       = resource.DependsOn,
                Extensions      = deploymentContext.Extensions,
                TenantId        = deploymentContext.TenantId
            };

            return(Validate(deployInput, functions, infrastructure));
        }
        public override async Task <TaskResult> RunTask(OrchestrationContext context, string arg)
        {
            DeploymentOrchestrationInput input = this.DataConverter.Deserialize <DeploymentOrchestrationInput>(arg);

            if (string.IsNullOrEmpty(input.RootId))
            {
                input.RootId = input.DeploymentId;
            }

            #region validate template

            // when Template had value, this orchestration call by internal, the template string content already be parsed
            if (input.Template == null || input.Deployments.Count > 0)
            {
                var valid = await context.ScheduleTask <TaskResult>(ValidateTemplateActivity.Name, "1.0", input);

                if (valid.Code != 200)
                {
                    return(valid);
                }
                input = DataConverter.Deserialize <DeploymentOrchestrationInput>(valid.Content);
            }
            else
            {
                helper.SaveDeploymentOperation(new DeploymentOperation(input, infrastructure)
                {
                    InstanceId  = context.OrchestrationInstance.InstanceId,
                    ExecutionId = context.OrchestrationInstance.ExecutionId,
                    Input       = arg,
                    Stage       = ProvisioningStage.ValidateTemplate
                });;
            }

            #endregion validate template

            #region InjectBeforeDeployment

            if (infrastructure.InjectBeforeDeployment)
            {
                var injectBeforeDeploymenteResult = await context.CreateSubOrchestrationInstance <TaskResult>(
                    RequestOrchestration.Name,
                    "1.0",
                    new AsyncRequestActivityInput()
                {
                    InstanceId        = context.OrchestrationInstance.InstanceId,
                    ExecutionId       = context.OrchestrationInstance.ExecutionId,
                    ProvisioningStage = ProvisioningStage.InjectBeforeDeployment,
                    DeploymentContext = input,
                    Resource          = null
                });

                if (injectBeforeDeploymenteResult.Code != 200)
                {
                    helper.SaveDeploymentOperation(new DeploymentOperation(input, infrastructure, null)
                    {
                        InstanceId  = context.OrchestrationInstance.InstanceId,
                        ExecutionId = context.OrchestrationInstance.ExecutionId,
                        Stage       = ProvisioningStage.BeforeDeploymentFailed,
                        Result      = DataConverter.Serialize(injectBeforeDeploymenteResult)
                    });
                    return(injectBeforeDeploymenteResult);
                }
            }

            #endregion InjectBeforeDeployment

            #region Before Deployment

            if (infrastructure.BeforeDeploymentOrchestration != null)
            {
                foreach (var t in infrastructure.BeforeDeploymentOrchestration)
                {
                    var r = await context.CreateSubOrchestrationInstance <TaskResult>(t.Name, t.Version, input);

                    if (r.Code != 200)
                    {
                        helper.SaveDeploymentOperation(new DeploymentOperation(input, infrastructure, null)
                        {
                            InstanceId  = context.OrchestrationInstance.InstanceId,
                            ExecutionId = context.OrchestrationInstance.ExecutionId,
                            Stage       = ProvisioningStage.BeforeDeploymentFailed,
                            Result      = DataConverter.Serialize(r)
                        });
                        return(r);
                    }

                    input = DataConverter.Deserialize <DeploymentOrchestrationInput>(r.Content);
                }
            }

            #endregion Before Deployment

            #region DependsOn

            if (input.DependsOn.Count > 0)
            {
                waitHandler = new TaskCompletionSource <string>();
                await context.ScheduleTask <TaskResult>(WaitDependsOnActivity.Name, "1.0",
                                                        new WaitDependsOnActivityInput()
                {
                    ProvisioningStage = ProvisioningStage.DependsOnWaited,
                    DeploymentContext = input,
                    Resource          = null,
                    DependsOn         = input.DependsOn
                });

                await waitHandler.Task;
                var r = DataConverter.Deserialize <TaskResult>(waitHandler.Task.Result);
                if (r.Code != 200)
                {
                    helper.SaveDeploymentOperation(new DeploymentOperation(input, infrastructure, null)
                    {
                        InstanceId  = context.OrchestrationInstance.InstanceId,
                        ExecutionId = context.OrchestrationInstance.ExecutionId,
                        Stage       = ProvisioningStage.DependsOnWaitedFailed,
                        Result      = DataConverter.Serialize(r)
                    });
                    return(r);
                }
            }

            #endregion DependsOn

            bool hasFailResource = false;

            #region Provisioning resources
            ConcurrentBag <Task <TaskResult> > tasks = new ConcurrentBag <Task <TaskResult> >();

            Dictionary <string, List <Resource> > copyDic = new Dictionary <string, List <Resource> >();
            foreach (var resource in input.Template.Resources)
            {
                if (resource.FullType == infrastructure.BuiltinServiceTypes.Deployments ||
                    resource.Type == Copy.ServiceType)
                {
                    continue;
                }
                if (!string.IsNullOrEmpty(resource.CopyId))
                {
                    if (!copyDic.TryGetValue(resource.CopyName, out List <Resource> rList))
                    {
                        rList = new List <Resource>();
                        copyDic.Add(resource.CopyName, rList);
                    }
                    rList.Add(resource);
                    continue;
                }
                tasks.Add(context.CreateSubOrchestrationInstance <TaskResult>(
                              ResourceOrchestration.Name,
                              "1.0",
                              new ResourceOrchestrationInput()
                {
                    Resource = resource,
                    Context  = input,
                }));
            }
            foreach (var deploy in input.Deployments)
            {
                tasks.Add(context.CreateSubOrchestrationInstance <TaskResult>(
                              DeploymentOrchestration.Name,
                              "1.0",
                              DataConverter.Serialize(deploy.Value)));
            }
            foreach (var key in copyDic.Keys)
            {
                var c = input.Template.Resources[key] as CopyResource;
                tasks.Add(context.CreateSubOrchestrationInstance <TaskResult>(CopyOrchestration.Name, "1.0", new CopyOrchestrationInput()
                {
                    Resource  = c,
                    Context   = input,
                    Resources = copyDic[key]
                }));
            }

            await Task.WhenAll(tasks.ToArray());

            foreach (var t in tasks)
            {
                if (t.Result.Code != 200)
                {
                    hasFailResource = true;
                    break;
                }
            }

            #endregion Provisioning resources

            if (input.Mode == DeploymentMode.Complete)
            {
                // TODO: complete mode, delete resource not exist in template
            }
            string rtv = null;

            #region After Deployment

            if (infrastructure.AfterDeploymentOrhcestration != null)
            {
                foreach (var t in infrastructure.AfterDeploymentOrhcestration)
                {
                    var r = await context.CreateSubOrchestrationInstance <TaskResult>(t.Name, t.Version, input);

                    if (r.Code != 200)
                    {
                        helper.SaveDeploymentOperation(new DeploymentOperation(input, infrastructure, null)
                        {
                            InstanceId  = context.OrchestrationInstance.InstanceId,
                            ExecutionId = context.OrchestrationInstance.ExecutionId,
                            Stage       = ProvisioningStage.AfterDeploymentOrhcestrationFailed,
                            Result      = DataConverter.Serialize(r)
                        });
                        return(r);
                    }
                    input = DataConverter.Deserialize <DeploymentOrchestrationInput>(r.Content);
                }
            }

            #endregion After Deployment

            if (infrastructure.InjectAfterDeployment)
            {
                var injectAfterDeploymenteResult = await context.CreateSubOrchestrationInstance <TaskResult>(
                    RequestOrchestration.Name,
                    "1.0",
                    new AsyncRequestActivityInput()
                {
                    InstanceId        = context.OrchestrationInstance.InstanceId,
                    ExecutionId       = context.OrchestrationInstance.ExecutionId,
                    ProvisioningStage = ProvisioningStage.InjectAfterDeployment,
                    DeploymentContext = input,
                    Resource          = null
                });

                if (injectAfterDeploymenteResult.Code != 200)
                {
                    return(injectAfterDeploymenteResult);
                }
            }

            #region get template outputs

            if (!string.IsNullOrEmpty(input.Template.Outputs))
            {
                try
                {
                    rtv = this.GetOutputs(input);
                }
                catch (Exception ex)
                {
                    hasFailResource = true;
                    rtv             = ex.Message;
                }
            }

            #endregion get template outputs

            helper.SaveDeploymentOperation(new DeploymentOperation(input, infrastructure, null)
            {
                InstanceId  = context.OrchestrationInstance.InstanceId,
                ExecutionId = context.OrchestrationInstance.ExecutionId,
                Stage       = hasFailResource ? ProvisioningStage.Failed : ProvisioningStage.Successed,
                Result      = rtv
            });
            return(new TaskResult()
            {
                Code = hasFailResource ? 500 : 200, Content = rtv
            });
        }