Exemple #1
0
        public async Task ListStackAndCurrentlySelected()
        {
            var projectSettings = new ProjectSettings(
                $"node_list_test{GetTestSuffix()}",
                ProjectRuntimeName.NodeJS);

            using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
            {
                ProjectSettings      = projectSettings,
                EnvironmentVariables = new Dictionary <string, string?>()
                {
                    ["PULUMI_CONFIG_PASSPHRASE"] = "test",
                }
            });

            var stackNames = new List <string>();

            try
            {
                for (var i = 0; i < 2; i++)
                {
                    var stackName = GetStackName();
                    await WorkspaceStack.CreateAsync(stackName, workspace);

                    stackNames.Add(stackName);
                    var summary = await workspace.GetStackAsync();

                    Assert.NotNull(summary);
                    Assert.True(summary !.IsCurrent);
                    var stacks = await workspace.ListStacksAsync();

                    Assert.Equal(i + 1, stacks.Count);
                }
            }
            finally
            {
                foreach (var name in stackNames)
                {
                    await workspace.RemoveStackAsync(name);
                }
            }
        public async Task StackAlreadyExistsExceptionIsThrown()
        {
            var projectSettings = new ProjectSettings("command_exception_test", ProjectRuntimeName.NodeJS);

            using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
            {
                ProjectSettings = projectSettings,
            });

            var stackName = $"already_existing_stack{GetTestSuffix()}";
            await workspace.CreateStackAsync(stackName);

            try
            {
                var createTask = workspace.CreateStackAsync(stackName);
                await Assert.ThrowsAsync <StackAlreadyExistsException>(
                    () => createTask);
            }
            finally
            {
                await workspace.RemoveStackAsync(stackName);
            }
        }
Exemple #3
0
        public async Task GetStackSettings(string extension)
        {
            var workingDir = ResourcePath(Path.Combine("Data", extension));

            using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
            {
                WorkDir = workingDir,
            });

            var settings = await workspace.GetStackSettingsAsync("dev");

            Assert.NotNull(settings);
            Assert.Equal("abc", settings !.SecretsProvider);
            Assert.NotNull(settings.Config);

            Assert.True(settings.Config !.TryGetValue("plain", out var plainValue));
            Assert.Equal("plain", plainValue !.Value);
            Assert.False(plainValue.IsSecure);

            Assert.True(settings.Config.TryGetValue("secure", out var secureValue));
            Assert.Equal("secret", secureValue !.Value);
            Assert.True(secureValue.IsSecure);
        }
Exemple #4
0
        public async Task CreateSelectRemoveStack()
        {
            var projectSettings = new ProjectSettings("node_test", ProjectRuntimeName.NodeJS);

            using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
            {
                ProjectSettings      = projectSettings,
                EnvironmentVariables = new Dictionary <string, string>()
                {
                    ["PULUMI_CONFIG_PASSPHRASE"] = "test",
                }
            });

            var stackName = $"int_test{GetTestSuffix()}";

            var stacks = await workspace.ListStacksAsync();

            Assert.Empty(stacks);

            await workspace.CreateStackAsync(stackName);

            stacks = await workspace.ListStacksAsync();

            var newStack = stacks.FirstOrDefault(s => s.Name == stackName);

            Assert.NotNull(newStack);
            Assert.True(newStack.IsCurrent);

            await workspace.SelectStackAsync(stackName);

            await workspace.RemoveStackAsync(stackName);

            stacks = await workspace.ListStacksAsync();

            Assert.Empty(stacks);
        }
        public async Task ConcurrentUpdateExceptionIsThrown()
        {
            var projectSettings = new ProjectSettings("command_exception_test", ProjectRuntimeName.NodeJS);

            using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
            {
                ProjectSettings = projectSettings,
            });

            var stackName = $"concurrent_update_stack{GetTestSuffix()}";
            await workspace.CreateStackAsync(stackName);

            try
            {
                var stack = await WorkspaceStack.SelectAsync(stackName, workspace);

                var hitSemaphore = false;
                using var semaphore = new SemaphoreSlim(0, 1);
                var program = PulumiFn.Create(() =>
                {
                    hitSemaphore = true;
                    // ReSharper disable once AccessToDisposedClosure
                    semaphore.Wait();
                    return(new Dictionary <string, object?>
                    {
                        ["test"] = "doesnt matter",
                    });
                });

                var upTask = stack.UpAsync(new UpOptions
                {
                    Program = program,
                });

                // wait until we hit semaphore
                while (!hitSemaphore)
                {
                    await Task.Delay(TimeSpan.FromSeconds(2));

                    if (upTask.IsFaulted)
                    {
                        throw upTask.Exception !;
                    }
                    else if (upTask.IsCompleted)
                    {
                        throw new Exception("never hit semaphore in first UP task");
                    }
                }

                // before releasing the semaphore, ensure another up throws
                var concurrentTask = stack.UpAsync(new UpOptions
                {
                    Program = program, // should never make it into this
                });

                await Assert.ThrowsAsync <ConcurrentUpdateException>(
                    () => concurrentTask);

                // finish first up call
                semaphore.Release();
                await upTask;
            }
            finally
            {
                await workspace.RemoveStackAsync(stackName);
            }
        }
Exemple #6
0
        static async Task <int> DeployStack(
            string stack,
            string name,
            string tier,
            string track,
            string version,
            string image,
            int percentage
            )
        {
            var currentDir = Directory.GetCurrentDirectory();

            Information("Starting with {Name} {Stack} in {CurrentDir}", name, stack, currentDir);

            var customStackPath  = Path.Combine(currentDir, "deploy");
            var usingCustomStack = Directory.Exists(customStackPath);

            if (usingCustomStack)
            {
                Information("Using the custom stack in {Directory}", customStackPath);
            }

            LocalWorkspaceOptions stackArgs = usingCustomStack
                ? new LocalProgramArgs(stack, customStackPath)
                : new InlineProgramArgs(name, stack, PulumiFn.Create <DefaultStack>());

            using var workspace = await LocalWorkspace.CreateAsync(stackArgs);

            var appStack = await WorkspaceStack.CreateOrSelectAsync(stack, workspace);

            await appStack.RefreshAsync();

            Information("Configuring stack {Stack}", stack);

            var appSettings = new AutoDevOpsSettings.AppSettings(name, tier, track, version);

            var deploymentSettings = await Settings.GetDeploymentSettings();

            await appStack.SetJsonConfig("gitlab", Settings.GitLabSettings());

            await appStack.SetJsonConfig("registry", Settings.RegistrySettings(), true);

            await appStack.SetJsonConfig("app", appSettings);

            await appStack.SetJsonConfig("deploy", Settings.DeploySettings(image, percentage));

            await appStack.SetJsonConfig("service", deploymentSettings.Service);

            await appStack.SetJsonConfig("ingress", deploymentSettings.Ingress);

            await appStack.SetJsonConfig("prometheus", deploymentSettings.Prometheus);

            Information("Installing plugins");

            await appStack.Workspace.InstallPluginAsync("kubernetes", "v3.3.0");

            Information("Deploying stack {Stack}", stack);

            var result = await appStack.UpAsync(
                new UpOptions {
                OnStandardOutput = Information,
                OnStandardError  = Error
            }
                );

            Information("Deployment result: {Result}", result.Summary.Message);

            if (!Env.EnvironmentUrl.IsEmpty())
            {
                Information("Environment URL: {EnvironmentUrl}", Env.EnvironmentUrl);
            }

            return(result.Summary.Result == UpdateState.Succeeded ? 0 : -1);
        }
        static async Task Main(string[] args)
        {
            // define our pulumi program "inline"
            var program = PulumiFn.Create(() =>
            {
                // get default vpc
                var defaultVpc = Output.Create(Pulumi.Aws.Ec2.GetVpc.InvokeAsync(new Pulumi.Aws.Ec2.GetVpcArgs
                {
                    Default = true,
                }));

                // get public subnets
                var publicSubnetIds = defaultVpc.Apply(vpc =>
                {
                    return(Output.Create(Pulumi.Aws.Ec2.GetSubnetIds.InvokeAsync(new Pulumi.Aws.Ec2.GetSubnetIdsArgs
                    {
                        VpcId = vpc.Id,
                    })));
                });

                var subnetGroup = new Pulumi.Aws.Rds.SubnetGroup(
                    "db-subnet",
                    new Pulumi.Aws.Rds.SubnetGroupArgs
                {
                    SubnetIds = publicSubnetIds.Apply(x => x.Ids),
                });

                // make a public security group for our cluster for the migration
                var securityGroup = new Pulumi.Aws.Ec2.SecurityGroup(
                    "public-security-group",
                    new Pulumi.Aws.Ec2.SecurityGroupArgs
                {
                    Ingress = new Pulumi.Aws.Ec2.Inputs.SecurityGroupIngressArgs
                    {
                        Protocol   = "-1",
                        FromPort   = 0,
                        ToPort     = 0,
                        CidrBlocks = "0.0.0.0/0"
                    },
                    Egress = new Pulumi.Aws.Ec2.Inputs.SecurityGroupEgressArgs
                    {
                        Protocol   = "-1",
                        FromPort   = 0,
                        ToPort     = 0,
                        CidrBlocks = "0.0.0.0/0",
                    },
                });

                // for example, you should change this
                var dbName     = "hellosql";
                var dbUser     = "******";
                var dbPassword = "******";

                // provision our db
                var cluster = new Pulumi.Aws.Rds.Cluster(
                    "db",
                    new Pulumi.Aws.Rds.ClusterArgs
                {
                    Engine              = Pulumi.Aws.Rds.EngineType.AuroraMysql,
                    EngineVersion       = "5.7.mysql_aurora.2.03.2",
                    DatabaseName        = dbName,
                    MasterUsername      = dbUser,
                    MasterPassword      = dbPassword,
                    SkipFinalSnapshot   = true,
                    DbSubnetGroupName   = subnetGroup.Name,
                    VpcSecurityGroupIds = securityGroup.Id,
                });

                var clusterInstance = new Pulumi.Aws.Rds.ClusterInstance(
                    "db-instance",
                    new Pulumi.Aws.Rds.ClusterInstanceArgs
                {
                    ClusterIdentifier  = cluster.ClusterIdentifier,
                    InstanceClass      = Pulumi.Aws.Rds.InstanceType.T3_Small,
                    Engine             = Pulumi.Aws.Rds.EngineType.AuroraMysql.ToString(),
                    EngineVersion      = "5.7.mysql_aurora.2.03.2",
                    PubliclyAccessible = true,
                    DbSubnetGroupName  = subnetGroup.Name,
                });

                // export the website url
                return(new Dictionary <string, object?>
                {
                    ["host"] = cluster.Endpoint,
                    ["db_name"] = dbName,
                    ["db_user"] = dbUser,
                    ["db_pass"] = dbPassword,
                });
            });

            // to destroy our program, we can run "dotnet run destroy"
            var destroy = args.Any() && args[0] == "destroy";

            var projectName = "database_migration_project";
            var stackName   = "dev";

            // create or select a stack matching the specified name and project
            // this will set up a workspace with everything necessary to run our inline program (program)
            var stackArgs = new InlineProgramArgs(projectName, stackName, program);
            var stack     = await LocalWorkspace.CreateOrSelectStackAsync(stackArgs);

            Console.WriteLine("successfully initialized stack");

            // for inline programs, we must manage plugins ourselves
            Console.WriteLine("installing plugins...");
            await stack.Workspace.InstallPluginAsync("aws", "v4.24.1");

            Console.WriteLine("plugins installed");

            // set stack configuration specifying the region to deploy
            Console.WriteLine("setting up config...");
            await stack.SetConfigAsync("aws:region", new ConfigValue("us-west-2"));

            Console.WriteLine("config set");

            Console.WriteLine("refreshing stack...");
            await stack.RefreshAsync(new RefreshOptions { OnStandardOutput = Console.WriteLine });

            Console.WriteLine("refresh complete");

            if (destroy)
            {
                Console.WriteLine("destroying stack...");
                await stack.DestroyAsync(new DestroyOptions { OnStandardOutput = Console.WriteLine });

                Console.WriteLine("stack destroy complete");
                return;
            }

            Console.WriteLine("updating stack...");
            var result = await stack.UpAsync(new UpOptions { OnStandardOutput = Console.WriteLine });

            if (result.Summary.ResourceChanges != null)
            {
                Console.WriteLine("update summary:");
                foreach (var change in result.Summary.ResourceChanges)
                {
                    Console.WriteLine($"    {change.Key}: {change.Value}");
                }
            }

            Console.WriteLine($"db host url: {result.Outputs["host"].Value}");

            Console.WriteLine("configuring db...");
            var connectionStr = $"Server={result.Outputs["host"].Value};Database={result.Outputs["db_name"].Value};Uid={result.Outputs["db_user"].Value};Pwd={result.Outputs["db_pass"].Value};";

            using var connection = new MySqlConnection(connectionStr);
            await connection.OpenAsync();

            Console.WriteLine("db configured!");

            // make sure the table exists
            const string createTableQuery = @"
CREATE TABLE IF NOT EXISTS hello_pulumi(
id int(9) NOT NULL PRIMARY KEY,
color varchar(14) NOT NULL);";

            using var createCommand = new MySqlCommand(createTableQuery, connection);
            await createCommand.ExecuteNonQueryAsync();

            // seed the table with some data to start
            const string seedTableQuery = @"
INSERT IGNORE INTO hello_pulumi (id, color)
VALUES
    (1, 'Purple'),
    (2, 'Violet'),
    (3, 'Plum');";

            using var seedCommand = new MySqlCommand(seedTableQuery, connection);
            await seedCommand.ExecuteNonQueryAsync();

            Console.WriteLine("rows inserted!");
            Console.WriteLine("querying to verify data...");

            const string readTableQuery = "SELECT COUNT(*) FROM hello_pulumi;";

            using var readCommand = new MySqlCommand(readTableQuery, connection);
            var readResult = await readCommand.ExecuteScalarAsync();

            Console.WriteLine($"Result: {readResult} rows");

            Console.WriteLine("database, table, and rows successfully configured");
        }
Exemple #8
0
        public async Task ManipulateConfig()
        {
            var projectName     = "manipulate_config_test";
            var projectSettings = new ProjectSettings(projectName, ProjectRuntimeName.NodeJS);

            using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
            {
                ProjectSettings      = projectSettings,
                EnvironmentVariables = new Dictionary <string, string?>()
                {
                    ["PULUMI_CONFIG_PASSPHRASE"] = "test",
                }
            });

            var stackName = $"{RandomStackName()}";
            var stack     = await WorkspaceStack.CreateAsync(stackName, workspace);

            var config = new Dictionary <string, ConfigValue>()
            {
                ["plain"]  = new ConfigValue("abc"),
                ["secret"] = new ConfigValue("def", isSecret: true),
            };

            var plainKey  = NormalizeConfigKey("plain", projectName);
            var secretKey = NormalizeConfigKey("secret", projectName);

            try
            {
                await Assert.ThrowsAsync <CommandException>(
                    () => stack.GetConfigAsync(plainKey));

                var values = await stack.GetAllConfigAsync();

                Assert.Empty(values);

                await stack.SetAllConfigAsync(config);

                values = await stack.GetAllConfigAsync();

                Assert.True(values.TryGetValue(plainKey, out var plainValue));
                Assert.Equal("abc", plainValue !.Value);
                Assert.False(plainValue.IsSecret);
                Assert.True(values.TryGetValue(secretKey, out var secretValue));
                Assert.Equal("def", secretValue !.Value);
                Assert.True(secretValue.IsSecret);

                // Get individual configuration values
                plainValue = await stack.GetConfigAsync(plainKey);

                Assert.Equal("abc", plainValue !.Value);
                Assert.False(plainValue.IsSecret);

                secretValue = await stack.GetConfigAsync(secretKey);

                Assert.Equal("def", secretValue !.Value);
                Assert.True(secretValue.IsSecret);

                await stack.RemoveConfigAsync("plain");

                values = await stack.GetAllConfigAsync();

                Assert.Single(values);

                await stack.SetConfigAsync("foo", new ConfigValue("bar"));

                values = await stack.GetAllConfigAsync();

                Assert.Equal(2, values.Count);
            }
            finally
            {
                await workspace.RemoveStackAsync(stackName);
            }
        }
        static async Task Main(string[] args)
        {
            // define our pulumi program "inline"
            var program = PulumiFn.Create(() =>
            {
                // create a bucket and expose a website index document
                var siteBucket = new Pulumi.Aws.S3.Bucket(
                    "s3-website-bucket",
                    new Pulumi.Aws.S3.BucketArgs
                {
                    Website = new Pulumi.Aws.S3.Inputs.BucketWebsiteArgs
                    {
                        IndexDocument = "index.html",
                    },
                });

                const string indexContent = @"
<html>
    <head><titl>Hello S3</title><meta charset=""UTF-8""></head>
    <body>
        <p>Hello, world!</p>
        <p>Made with ❤️ with <a href=""https://pulumi.com"">Pulumi</a></p>
    </body>
</html>
";

                // write our index.html into the site bucket
                var @object = new Pulumi.Aws.S3.BucketObject(
                    "index",
                    new Pulumi.Aws.S3.BucketObjectArgs
                {
                    Bucket      = siteBucket.BucketName,      // reference to the s3 bucket object
                    Content     = indexContent,
                    Key         = "index.html",               // set the key of the object
                    ContentType = "text/html; charset=utf-8", // set the MIME type of the file
                });

                var bucketPolicyDocument = siteBucket.Arn.Apply(bucketArn =>
                {
                    return(Output.Create(Pulumi.Aws.Iam.GetPolicyDocument.InvokeAsync(
                                             new Pulumi.Aws.Iam.GetPolicyDocumentArgs
                    {
                        Statements = new List <Pulumi.Aws.Iam.Inputs.GetPolicyDocumentStatementArgs>
                        {
                            new Pulumi.Aws.Iam.Inputs.GetPolicyDocumentStatementArgs
                            {
                                Effect = "Allow",
                                Principals = new List <Pulumi.Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalArgs>
                                {
                                    new Pulumi.Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalArgs
                                    {
                                        Identifiers = new List <string> {
                                            "*"
                                        },
                                        Type = "AWS",
                                    },
                                },
                                Actions = new List <string> {
                                    "s3:GetObject"
                                },
                                Resources = new List <string> {
                                    $"{bucketArn}/*"
                                },
                            },
                        },
                    })));
                });

                // set the access policy for the bucket so all objects are readable
                new Pulumi.Aws.S3.BucketPolicy(
                    "bucket-policy",
                    new Pulumi.Aws.S3.BucketPolicyArgs
                {
                    Bucket = siteBucket.BucketName,
                    Policy = bucketPolicyDocument.Apply(x => x.Json),
                });

                // export the website url
                return(new Dictionary <string, object?>
                {
                    ["secret"] = Pulumi.Output.CreateSecret("hello world"),
                    ["website_url"] = siteBucket.WebsiteEndpoint,
                });
            });

            // to destroy our program, we can run "dotnet run destroy"
            var destroy = args.Any() && args[0] == "destroy";

            var projectName     = "inline_s3_project";
            var stackName       = "dev";
            var secretsProvider = "awskms://aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee?region=us-west-2";
            var keyId           = System.Environment.GetEnvironmentVariable("KMS_KEY");

            if (keyId != null)
            {
                secretsProvider = "awskms://" + keyId + "?region=" + System.Environment.GetEnvironmentVariable("AWS_REGION");
            }

            if (secretsProvider == "awskms://aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee?region=us-west-2")
            {
                throw new Exception("Update secretsProvider to use an actual AWS KMS key.");
            }

            // create or select a stack matching the specified name and project
            // this will set up a workspace with everything necessary to run our inline program (program)
            var stackArgs = new InlineProgramArgs(projectName, stackName, program);

            stackArgs.SecretsProvider = secretsProvider;
            stackArgs.StackSettings   = new Dictionary <string, StackSettings>()
            {
                [stackName] = new StackSettings()
                {
                    SecretsProvider = secretsProvider
                }
            };

            var stack = await LocalWorkspace.CreateOrSelectStackAsync(stackArgs);

            Console.WriteLine("successfully initialized stack");

            // for inline programs, we must manage plugins ourselves
            Console.WriteLine("installing plugins...");
            await stack.Workspace.InstallPluginAsync("aws", "v4.24.1");

            Console.WriteLine("plugins installed");

            // set stack configuration specifying the region to deploy
            Console.WriteLine("setting up config...");
            await stack.SetConfigAsync("aws:region", new ConfigValue("us-west-2"));

            Console.WriteLine("config set");

            Console.WriteLine("refreshing stack...");
            await stack.RefreshAsync(new RefreshOptions { OnStandardOutput = Console.WriteLine });

            Console.WriteLine("refresh complete");

            if (destroy)
            {
                Console.WriteLine("destroying stack...");
                await stack.DestroyAsync(new DestroyOptions { OnStandardOutput = Console.WriteLine });

                Console.WriteLine("stack destroy complete");
            }
            else
            {
                Console.WriteLine("updating stack...");
                var result = await stack.UpAsync(new UpOptions { OnStandardOutput = Console.WriteLine });

                if (result.Summary.ResourceChanges != null)
                {
                    Console.WriteLine("update summary:");
                    foreach (var change in result.Summary.ResourceChanges)
                    {
                        Console.WriteLine($"    {change.Key}: {change.Value}");
                    }
                }

                Console.WriteLine($"website url: {result.Outputs["website_url"].Value}");
            }
        }