├── .flake8 ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── COPYING ├── README.md ├── coverage-tests.py ├── default.nix ├── examples ├── apache-ec2-multizone.nix ├── apache-ec2.nix ├── apache.nix ├── aws-data-lifecycle-manager.nix ├── cloudwatch-logs.nix ├── cloudwatch-metric-alarm.nix ├── drbd.nix ├── ec2-info-example.nix ├── ec2-rds-dbinstance.nix ├── elastic-file-system.nix ├── encryption-test.nix ├── iam-role.nix ├── mediawiki-ec2.nix ├── mediawiki.nix ├── nix-homepage.nix ├── route53.nix ├── s3-bucket.nix ├── sns-topic.nix ├── terminal-server-ec2.nix ├── terminal-server.nix ├── trivial-ec2-ebs.nix ├── trivial-ec2.nix ├── trivial.nix ├── vpc.nix ├── webdslorg-lb-ec2.nix └── webdslorg-lb.nix ├── maintainers └── dump-route53-hosted-zone.py ├── nixops_aws ├── __init__.py ├── backends │ ├── __init__.py │ ├── ec2.py │ └── options.py ├── ec2_utils.py ├── nix │ ├── aws-data-lifecycle-manager.nix │ ├── aws-vpn-connection-route.nix │ ├── aws-vpn-connection.nix │ ├── aws-vpn-gateway.nix │ ├── cloudwatch-log-group.nix │ ├── cloudwatch-log-stream.nix │ ├── cloudwatch-metric-alarm.nix │ ├── common-ebs-options.nix │ ├── common-ec2-auth-options.nix │ ├── common-ec2-options.nix │ ├── default.nix │ ├── ebs-volume.nix │ ├── ec2-keypair.nix │ ├── ec2-placement-group.nix │ ├── ec2-properties.nix │ ├── ec2-rds-dbinstance.nix │ ├── ec2-rds-dbsecurity-group.nix │ ├── ec2-security-group.nix │ ├── ec2.nix │ ├── elastic-file-system-mount-target.nix │ ├── elastic-file-system.nix │ ├── elastic-ip.nix │ ├── generate-ec2-properties.nix │ ├── generate-ec2-properties.py │ ├── generate-ec2-properties.sh │ ├── iam-role.nix │ ├── lib.nix │ ├── rds-subnet-group.nix │ ├── route53-health-check.nix │ ├── route53-hosted-zone.nix │ ├── route53-recordset.nix │ ├── route53.nix │ ├── s3-bucket.nix │ ├── sns-topic.nix │ ├── sqs-queue.nix │ ├── vpc-customer-gateway.nix │ ├── vpc-dhcp-options.nix │ ├── vpc-egress-only-internet-gateway.nix │ ├── vpc-endpoint.nix │ ├── vpc-internet-gateway.nix │ ├── vpc-nat-gateway.nix │ ├── vpc-network-acl.nix │ ├── vpc-network-interface-attachment.nix │ ├── vpc-network-interface.nix │ ├── vpc-route-table-association.nix │ ├── vpc-route-table.nix │ ├── vpc-route.nix │ ├── vpc-subnet.nix │ └── vpc.nix ├── plugin.py └── resources │ ├── __init__.py │ ├── aws_data_lifecycle_manager.py │ ├── aws_vpn_connection.py │ ├── aws_vpn_connection_route.py │ ├── aws_vpn_gateway.py │ ├── cloudwatch_log_group.py │ ├── cloudwatch_log_stream.py │ ├── cloudwatch_metric_alarm.py │ ├── ebs_volume.py │ ├── ec2_common.py │ ├── ec2_keypair.py │ ├── ec2_placement_group.py │ ├── ec2_rds_dbinstance.py │ ├── ec2_rds_dbsecurity_group.py │ ├── ec2_security_group.py │ ├── efs_common.py │ ├── elastic_file_system.py │ ├── elastic_file_system_mount_target.py │ ├── elastic_ip.py │ ├── iam_role.py │ ├── rds_db_subnet_group.py │ ├── route53_health_check.py │ ├── route53_hosted_zone.py │ ├── route53_recordset.py │ ├── s3_bucket.py │ ├── sns_topic.py │ ├── sqs_queue.py │ ├── types │ ├── __init__.py │ ├── aws_data_lifecycle_manager.py │ ├── aws_vpn_connection.py │ ├── aws_vpn_connection_route.py │ ├── aws_vpn_gateway.py │ ├── cloudwatch_log_group.py │ ├── cloudwatch_log_stream.py │ ├── cloudwatch_metric_alarm.py │ ├── ebs_volume.py │ ├── ec2_keypair.py │ ├── ec2_placement_group.py │ ├── ec2_rds_dbinstance.py │ ├── ec2_rds_dbsecurity_group.py │ ├── ec2_security_group.py │ ├── elastic_file_system.py │ ├── elastic_file_system_mount_target.py │ ├── elastic_ip.py │ ├── iam_role.py │ ├── rds_db_subnet_group.py │ ├── route53_health_check.py │ ├── route53_hosted_zone.py │ ├── route53_recordset.py │ ├── s3_bucket.py │ ├── sns_topic.py │ ├── sqs_queue.py │ ├── vpc.py │ ├── vpc_customer_gateway.py │ ├── vpc_dhcp_options.py │ ├── vpc_egress_only_internet_gateway.py │ ├── vpc_endpoint.py │ ├── vpc_internet_gateway.py │ ├── vpc_nat_gateway.py │ ├── vpc_network_acl.py │ ├── vpc_network_interface.py │ ├── vpc_network_interface_attachment.py │ ├── vpc_route.py │ ├── vpc_route_table.py │ ├── vpc_route_table_association.py │ └── vpc_subnet.py │ ├── vpc.py │ ├── vpc_customer_gateway.py │ ├── vpc_dhcp_options.py │ ├── vpc_egress_only_internet_gateway.py │ ├── vpc_endpoint.py │ ├── vpc_internet_gateway.py │ ├── vpc_nat_gateway.py │ ├── vpc_network_acl.py │ ├── vpc_network_interface.py │ ├── vpc_network_interface_attachment.py │ ├── vpc_route.py │ ├── vpc_route_table.py │ ├── vpc_route_table_association.py │ └── vpc_subnet.py ├── overrides.nix ├── poetry.lock ├── pyproject.toml ├── release.nix ├── setup.cfg ├── shell.nix ├── tests.py └── tests ├── __init__.py ├── functional ├── __init__.py ├── ec2-rds-dbinstance-with-sg.nix ├── ec2-rds-dbinstance.nix ├── ec2_with_nvme_device_mapping.nix ├── single_machine_ec2_base.nix ├── single_machine_ec2_base_nvme.nix ├── single_machine_ec2_ebs.nix ├── single_machine_ec2_raid-0-nvme.nix ├── single_machine_ec2_raid-0.nix ├── single_machine_ec2_spot_instance.nix ├── single_machine_logical_base.nix ├── test_backups.py ├── test_deploys_spot_instance.py ├── test_ec2_rds_dbinstance.py ├── test_ec2_with_nvme_device_mapping.py ├── vpc.nix └── vpc.py └── unit └── test_device_name_to_boto_expected.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E203, E266, E501, W503 3 | # line length is intentionally set to 80 here because black uses Bugbear 4 | # See https://github.com/psf/black/blob/master/README.md#line-length for more details 5 | max-line-length = 80 6 | max-complexity = 18 7 | select = B,C,E,F,W,T4,B9 8 | 9 | per-file-ignores = 10 | nixops/__main__.py:E402 11 | 12 | # # We need to configure the mypy.ini because the flake8-mypy's default 13 | # # options don't properly override it, so if we don't specify it we get 14 | # # half of the config from mypy.ini and half from flake8-mypy. 15 | # mypy_config = mypy.ini 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ "master" ] 5 | pull_request: 6 | branches: [ "**" ] 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2.3.4 13 | - name: Nix 14 | uses: cachix/install-nix-action@v12 15 | - name: Build 16 | run: 'nix-build -I nixpkgs=channel:nixos-20.09 --quiet release.nix -A nixops-aws.x86_64-linux --show-trace' 17 | env: 18 | NIX_PATH: "nixpkgs=channel:nixos-20.09" 19 | black: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v2.3.4 24 | - name: Nix 25 | uses: cachix/install-nix-action@v12 26 | - name: Black 27 | run: 'nix-shell ./shell.nix --run "black --check ."' 28 | env: 29 | NIX_PATH: "nixpkgs=channel:nixos-20.09" 30 | flake8: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2.3.4 35 | - name: Nix 36 | uses: cachix/install-nix-action@v12 37 | - name: Flake8 38 | run: 'nix-shell ./shell.nix --run "flake8 nixops_aws"' 39 | env: 40 | NIX_PATH: "nixpkgs=channel:nixos-20.09" 41 | mypy: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v2.3.4 46 | - name: Nix 47 | uses: cachix/install-nix-action@v12 48 | - name: Mypy 49 | run: 'nix-shell ./shell.nix --run "mypy nixops_aws"' 50 | env: 51 | NIX_PATH: "nixpkgs=channel:nixos-20.09" 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .coverage 3 | coverage.xml 4 | examples/*.json 5 | examples/*.lock 6 | html/ 7 | result 8 | tags 9 | tests/test.nixops* 10 | .mypy_cache 11 | 12 | syntax: glob 13 | .idea 14 | *egg-info 15 | *.log 16 | *.txt 17 | *.pyc 18 | *.swp 19 | *.csv 20 | *.out 21 | *.tar 22 | *.bkp 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NixOps AWS Plugin 2 | 3 | NixOps (formerly known as Charon) is a tool for deploying NixOS 4 | machines in a network or cloud. 5 | 6 | This repo contains the NixOps AWS Plugin. 7 | 8 | * [Manual](https://nixos.org/nixops/manual/) 9 | * [Installation](https://nixos.org/nixops/manual/#chap-installation) / [Hacking](https://nixos.org/nixops/manual/#chap-hacking) 10 | * [Continuous build](http://hydra.nixos.org/jobset/nixops/master#tabs-jobs) 11 | * [Source code](https://github.com/NixOS/nixops) 12 | * [Issue Tracker](https://github.com/NixOS/nixops/issues) 13 | * [Mailing list / Google group](https://groups.google.com/forum/#!forum/nixops-users) 14 | * [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org) 15 | 16 | ## Developing 17 | 18 | To start developing on the NixOps AWS plugin, you can run: 19 | 20 | ```bash 21 | $ nix-shell 22 | $ poetry install 23 | $ poetry shell 24 | ``` 25 | To view active plugins: 26 | 27 | ```bash 28 | nixops list-plugins 29 | ``` 30 | 31 | Documentation for the `nixops-aws` plugin is in the [nixops repo](https://github.com/NixOS/nixops) 32 | 33 | The python code is formatted with the latest release of [black](https://black.readthedocs.io/en/stable) 34 | and is checked in PR validation. See the `black` target in [ci.yaml](./github/workflows/ci.yaml) for the cmd to run. 35 | 36 | ## Building from source 37 | 38 | You can build the Nix package by simply invoking nix-build on the project root: 39 | 40 | ```bash 41 | $ nix-build 42 | ``` 43 | See the main NixOps repo for more up-to-date instructions for how to built NixOps 44 | with this AWS plugin. 45 | 46 | -------------------------------------------------------------------------------- /coverage-tests.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2 2 | import nose 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | nose.main( 7 | argv=[ 8 | sys.argv[0], 9 | "--with-coverage", 10 | "--cover-inclusive", 11 | "--cover-xml", 12 | "--cover-xml-file=coverage.xml", 13 | "--cover-html", 14 | "--cover-html-dir=./html", 15 | "--cover-package=nixops", 16 | "--nocapture", 17 | "-e", 18 | "^tests\.py$", 19 | ] 20 | + sys.argv[1:] 21 | ) 22 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | let 3 | overrides = import ./overrides.nix { inherit pkgs; }; 4 | in pkgs.poetry2nix.mkPoetryApplication { 5 | projectDir = ./.; 6 | overrides = pkgs.poetry2nix.overrides.withDefaults overrides; 7 | meta.description = "Nix package for ${pkgs.stdenv.system}"; 8 | } 9 | -------------------------------------------------------------------------------- /examples/apache-ec2-multizone.nix: -------------------------------------------------------------------------------- 1 | let 2 | 3 | configUS = 4 | { imports = [ ./ec2-info.nix ]; 5 | deployment.targetEnv = "ec2"; 6 | deployment.ec2.region = "us-east-1"; 7 | deployment.ec2.instanceType = "m1.small"; 8 | deployment.ec2.privateKey = "/home/eelco/.ec2/logicblox/id_rsa-eelco-logicblox-us-east-1"; 9 | }; 10 | 11 | configEU = 12 | { imports = [ ./ec2-info.nix ]; 13 | deployment.targetEnv = "ec2"; 14 | deployment.ec2.region = "eu-west-1"; 15 | deployment.ec2.instanceType = "m1.small"; 16 | }; 17 | 18 | # Run this machine under a different account. 19 | configEU_eelco = 20 | { imports = [ ./ec2-info-2.nix ]; 21 | deployment.targetEnv = "ec2"; 22 | deployment.ec2.region = "eu-west-1"; 23 | deployment.ec2.instanceType = "m1.small"; 24 | }; 25 | 26 | in 27 | 28 | { 29 | proxy = configEU; 30 | backend1 = configEU_eelco; 31 | backend2 = configUS; 32 | } 33 | -------------------------------------------------------------------------------- /examples/apache-ec2.nix: -------------------------------------------------------------------------------- 1 | { 2 | defaults = 3 | { config, pkgs, ... }: 4 | { imports = [ ./ec2-info-example.nix ]; 5 | deployment.targetEnv = "ec2"; 6 | deployment.ec2.region = pkgs.lib.mkDefault "eu-west-1"; 7 | deployment.ec2.instanceType = "t2.large"; 8 | }; 9 | 10 | backend2 = { ... }: { 11 | deployment.ec2.region = "us-east-1"; 12 | deployment.ec2.tags.DummyTag = "some random blabla"; 13 | deployment.ec2.tags.AnotherTag = "more blabla"; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /examples/apache.nix: -------------------------------------------------------------------------------- 1 | let 2 | 3 | backend = 4 | { config, pkgs, ... }: 5 | { imports = [ ./nix-homepage.nix ]; 6 | networking.firewall.allowedTCPPorts = [ 80 ]; 7 | }; 8 | 9 | in 10 | 11 | { 12 | network.description = "Load balancer test"; 13 | 14 | proxy = 15 | { config, pkgs, nodes, ... }: 16 | 17 | { 18 | services.httpd.enable = true; 19 | services.httpd.adminAddr = "e.dolstra@tudelft.nl"; 20 | services.httpd.extraModules = ["proxy_balancer"]; 21 | 22 | services.httpd.extraConfig = 23 | '' 24 | ExtendedStatus on 25 | 26 | 27 | Order deny,allow 28 | Allow from all 29 | SetHandler server-status 30 | 31 | 32 | 33 | Allow from all 34 | BalancerMember http://backend1 retry=0 35 | BalancerMember http://backend2 retry=0 36 | 37 | 38 | ProxyStatus full 39 | ProxyPass /server-status ! 40 | ProxyPass / balancer://cluster/ 41 | ProxyPassReverse / balancer://cluster/ 42 | 43 | # For testing; don't want to wait forever for dead backend servers. 44 | ProxyTimeout 5 45 | ''; 46 | 47 | networking.firewall.allowedTCPPorts = [ 80 ]; 48 | }; 49 | 50 | backend1 = backend; 51 | backend2 = backend; 52 | } 53 | -------------------------------------------------------------------------------- /examples/aws-data-lifecycle-manager.nix: -------------------------------------------------------------------------------- 1 | { region ? "us-east-1" 2 | , accessKeyId ? "testing" 3 | , ... 4 | }: 5 | { 6 | network.description = "AWS DLM Testing"; 7 | resources.awsDataLifecycleManager.testDataLifecycleManager = 8 | { 9 | inherit region accessKeyId; 10 | dlmName = "dlm-test"; 11 | description = "some description"; 12 | executionRole = "AWSDataLifecycleManagerDefaultRole"; 13 | resourceTypes = "instance"; 14 | targetTags.foo = "bar"; 15 | excludeBootVolume = true; 16 | copyTags = true; 17 | tagsToAdd.bar = "stuff"; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /examples/cloudwatch-logs.nix: -------------------------------------------------------------------------------- 1 | { account 2 | , region ? "us-east-1" 3 | , description ? "CloudWatch example" 4 | , ... 5 | }: 6 | { 7 | network.description = description; 8 | 9 | resources.cloudwatchLogStreams.stream = {resources,...}: { 10 | name="nixops-stream"; 11 | accessKeyId = account; 12 | logGroupName="${resources.cloudwatchLogGroups.log-group.name}"; 13 | inherit region; 14 | }; 15 | 16 | resources.cloudwatchLogGroups.log-group = { 17 | name="nixops-cloudwatch"; 18 | retentionInDays=30; 19 | accessKeyId = account; 20 | inherit region; 21 | }; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /examples/cloudwatch-metric-alarm.nix: -------------------------------------------------------------------------------- 1 | { accessKeyId ? "nixos-tests" 2 | , region ? "us-east-1" 3 | , ... 4 | }: 5 | { 6 | require = [ ./trivial-ec2.nix ]; 7 | 8 | resources.snsTopics.alert-topic = { 9 | displayName = "SNS alert topic"; 10 | subscriptions = [ 11 | { 12 | protocol = "email"; 13 | endpoint = "rob.vermaas+alerts@gmail.com"; 14 | } 15 | ]; 16 | inherit region accessKeyId; 17 | }; 18 | 19 | resources.cloudwatchMetricAlarms.my-alarm = 20 | { resources, ... }: 21 | { metricName = "StatusCheckFailed"; 22 | namespace = "AWS/EC2"; 23 | statistic = "Maximum"; 24 | dimensions = [ { Name = "InstanceId"; Value = resources.machines.machine; } ]; 25 | unit = "Count"; 26 | period = 300; 27 | evaluationPeriods = 2; 28 | threshold = 1; 29 | comparisonOperator = "GreaterThanOrEqualToThreshold"; 30 | datapointsToAlarm = 1; 31 | alarmActions = [ resources.snsTopics.alert-topic ]; 32 | insufficientDataActions = [ resources.snsTopics.alert-topic ]; 33 | 34 | inherit region accessKeyId; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /examples/drbd.nix: -------------------------------------------------------------------------------- 1 | let 2 | 3 | drbdConf = nodes: 4 | '' 5 | global { 6 | usage-count no; 7 | } 8 | 9 | resource r0 { 10 | protocol C; 11 | syncer { 12 | verify-alg sha1; 13 | } 14 | floating ${nodes.webserver.config.networking.privateIPv4}:7789 { 15 | device /dev/drbd1; 16 | disk /dev/loop0; 17 | meta-disk internal; 18 | } 19 | floating ${nodes.webserver_failover.config.networking.privateIPv4}:7789 { 20 | device /dev/drbd1; 21 | disk /dev/loop0; 22 | meta-disk internal; 23 | } 24 | } 25 | ''; 26 | 27 | loopUp = pkgs: 28 | { name = "loop-up"; 29 | task = true; 30 | startOn = "mounted MOUNTPOINT=/ephemeral0"; 31 | script = "${pkgs.utillinux}/sbin/losetup /dev/loop0 /ephemeral0/backing && start drbd-up"; 32 | }; 33 | 34 | in { 35 | 36 | webserver = 37 | { pkgs, nodes, ... }: 38 | { imports = [ ./ec2-info.nix ]; 39 | deployment.targetEnv = "ec2"; 40 | deployment.ec2.region = "us-east-1"; 41 | deployment.ec2.instanceType = "m1.large"; 42 | services.drbd.enable = true; 43 | services.drbd.config = drbdConf nodes; 44 | jobs.loopUp = loopUp pkgs; 45 | }; 46 | 47 | webserver_failover = 48 | { pkgs, nodes, ... }: 49 | { imports = [ ./ec2-info.nix ]; 50 | deployment.targetEnv = "ec2"; 51 | deployment.ec2.region = "us-east-1"; 52 | deployment.ec2.instanceType = "m1.large"; 53 | services.drbd.enable = true; 54 | services.drbd.config = drbdConf nodes; 55 | jobs.loopUp = loopUp pkgs; 56 | }; 57 | 58 | } 59 | -------------------------------------------------------------------------------- /examples/ec2-info-example.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | with pkgs.lib; 4 | 5 | { deployment.ec2.accessKeyId = "AKIA..."; 6 | deployment.ec2.keyPair = "..."; 7 | deployment.ec2.privateKey = mkDefault "/home/eelco/.ssh/id_rsa-ec2-${config.deployment.ec2.region}"; 8 | deployment.ec2.securityGroups = mkDefault [ "default" ]; 9 | } 10 | -------------------------------------------------------------------------------- /examples/ec2-rds-dbinstance.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | accessKeyId = "AKIA..."; 4 | in 5 | { 6 | network.description = "NixOps RDS Testing"; 7 | 8 | resources.rdsDbSecurityGroups.test-rds-sg = 9 | { 10 | inherit region accessKeyId; 11 | groupName = "test-nixops"; 12 | description = "testing sg for rds"; 13 | rules = [ 14 | { 15 | cidrIp = "0.0.0.0/0"; 16 | } 17 | ]; 18 | }; 19 | 20 | resources.rdsDbInstances.test-rds-instance = 21 | { resources, ... }: 22 | { 23 | inherit region accessKeyId; 24 | id = "test-multi-az"; 25 | instanceClass = "db.r3.large"; 26 | allocatedStorage = 30; 27 | masterUsername = "administrator"; 28 | masterPassword = "testing123"; 29 | port = 5432; 30 | engine = "postgres"; 31 | dbName = "testNixOps"; 32 | multiAZ = true; 33 | securityGroups = [ resources.rdsDbSecurityGroups.test-rds-sg ]; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /examples/elastic-file-system.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "eu-west-1"; 3 | accessKeyId = "eelco"; 4 | subnet = "subnet-14930963"; 5 | in 6 | 7 | { 8 | 9 | resources.ec2KeyPairs.default = 10 | { inherit region accessKeyId; 11 | }; 12 | 13 | resources.elasticFileSystems.filesystem = 14 | { inherit region accessKeyId; 15 | tags.Name = "My Test FS"; 16 | }; 17 | 18 | resources.elasticFileSystemMountTargets.test-mount = 19 | { resources, ... }: 20 | { inherit region accessKeyId subnet; 21 | fileSystem = resources.elasticFileSystems.filesystem; 22 | securityGroups = [ "default" ]; 23 | }; 24 | 25 | machine = 26 | { config, pkgs, resources, ... }: 27 | { deployment.targetEnv = "ec2"; 28 | deployment.ec2.instanceType = "t2.large"; 29 | deployment.ec2.region = region; 30 | deployment.ec2.accessKeyId = accessKeyId; 31 | deployment.ec2.securityGroups = [ "default" ]; 32 | deployment.ec2.subnetId = subnet; 33 | deployment.ec2.associatePublicIpAddress = true; 34 | deployment.ec2.securityGroupIds = [ "default" ]; 35 | deployment.ec2.keyPair = resources.ec2KeyPairs.default; 36 | deployment.ec2.tags.Name = "EFS test"; 37 | boot.supportedFilesystems = [ "nfs4" ]; 38 | 39 | fileSystems."/efs" = 40 | { fsType = "nfs"; 41 | device = "${resources.elasticFileSystemMountTargets.test-mount.ipAddress}:/"; 42 | options = [ "nfsvers=4.1" ]; 43 | }; 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /examples/encryption-test.nix: -------------------------------------------------------------------------------- 1 | { 2 | network.description = "Encryption test"; 3 | 4 | machine1 = 5 | { config, pkgs, ... }: 6 | { deployment.targetEnv = "virtualbox"; 7 | deployment.encryptedLinksTo = [ "machine2" "machine3" ]; 8 | }; 9 | 10 | machine2 = 11 | { config, pkgs, ... }: 12 | { deployment.targetEnv = "virtualbox"; 13 | deployment.encryptedLinksTo = [ "machine1" "machine3" ]; 14 | 15 | services.httpd.enable = true; 16 | services.httpd.adminAddr = "e.dolstra@tudelft.nl"; 17 | services.httpd.documentRoot = "/tmp"; 18 | }; 19 | 20 | machine3 = 21 | { config, pkgs, ... }: 22 | { deployment.targetEnv = "virtualbox"; 23 | deployment.encryptedLinksTo = [ "machine1" "machine2" ]; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /examples/iam-role.nix: -------------------------------------------------------------------------------- 1 | { account 2 | , description ? "IAM Role example" 3 | , ... 4 | }: 5 | { 6 | network.description = description; 7 | 8 | resources.iamRoles.role = { 9 | accessKeyId = account; 10 | tags = { 11 | Name = "SuperRole"; 12 | Category = "Example"; 13 | }; 14 | policy = 15 | builtins.toJSON 16 | { 17 | Statement = 18 | [ 19 | { 20 | Effect = "Allow"; 21 | Action = [ "ses:SendEmail" "ses:SendRawEmail"]; 22 | Resource = "*"; 23 | } 24 | { 25 | Effect = "Allow"; 26 | Action = [ "sns:Publish" "sns:ListTopics"]; 27 | Resource = "*"; 28 | } 29 | { 30 | Effect = "Allow"; 31 | Action = [ "cloudwatch:PutMetricData" ]; 32 | Resource = "*" ; 33 | } 34 | { 35 | Effect = "Allow"; 36 | Action = [ "ec2:DescribeTags" ]; 37 | Resource = "*" ; 38 | } 39 | ]; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /examples/mediawiki-ec2.nix: -------------------------------------------------------------------------------- 1 | let 2 | 3 | config = 4 | { imports = [ ./ec2-info.nix ]; 5 | deployment.targetEnv = "ec2"; 6 | deployment.ec2.region = "us-east-1"; 7 | deployment.ec2.instanceType = "m1.large"; 8 | }; 9 | 10 | in 11 | 12 | { 13 | webserver = config; 14 | database = config; 15 | } 16 | -------------------------------------------------------------------------------- /examples/mediawiki.nix: -------------------------------------------------------------------------------- 1 | { 2 | network.description = "Mediawiki network"; 3 | 4 | webserver = 5 | { config, pkgs, ... }: 6 | 7 | with pkgs.lib; 8 | 9 | { services.httpd.enable = true; 10 | services.httpd.adminAddr = "e.dolstra@tudelft.nl"; 11 | services.httpd.extraSubservices = singleton 12 | { serviceType = "mediawiki"; 13 | siteName = "Example Wiki"; 14 | dbServer = "database"; 15 | logo = "http://nixos.org/logo/nix-wiki.png"; 16 | }; 17 | environment.systemPackages = [ pkgs.postgresql ]; 18 | services.oidentd.enable = true; 19 | }; 20 | 21 | database = 22 | { config, pkgs, nodes, ... }: 23 | 24 | with pkgs.lib; 25 | 26 | let 27 | 28 | # !!! Cut&paste, extremely ugly. 29 | # Unpack Mediawiki and put the config file in its root directory. 30 | mediawikiRoot = pkgs.stdenv.mkDerivation rec { 31 | name= "mediawiki-1.15.5"; 32 | 33 | src = pkgs.fetchurl { 34 | url = "http://download.wikimedia.org/mediawiki/1.15/${name}.tar.gz"; 35 | sha256 = "1d8afbdh3lsg54b69mnh6a47psb3lg978xpp277qs08yz15cjf7q"; 36 | }; 37 | 38 | buildPhase = "true"; 39 | 40 | installPhase = 41 | '' 42 | mkdir -p $out 43 | cp -r * $out 44 | ''; 45 | }; 46 | 47 | in 48 | 49 | { services.postgresql.enable = true; 50 | services.postgresql.enableTCPIP = true; 51 | services.postgresql.authentication = 52 | '' 53 | local all root ident sameuser 54 | local mediawiki mediawiki ident mediawiki-map 55 | host all root ${nodes.webserver.config.networking.privateIPv4}/32 ident sameuser 56 | host mediawiki mediawiki ${nodes.webserver.config.networking.privateIPv4}/32 ident mediawiki-map 57 | ''; 58 | services.postgresql.identMap = 59 | '' 60 | mediawiki-map root mediawiki 61 | mediawiki-map wwwrun mediawiki 62 | ''; 63 | 64 | jobs.init_mediawiki_db = 65 | { task = true; 66 | startOn = "started postgresql"; 67 | script = 68 | '' 69 | mkdir -p /var/lib/psql-schemas 70 | if ! [ -e /var/lib/psql-schemas/mediawiki-created ]; then 71 | ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole mediawiki 72 | ${pkgs.postgresql}/bin/createdb mediawiki -O mediawiki 73 | ( echo 'CREATE LANGUAGE plpgsql;' 74 | cat ${mediawikiRoot}/maintenance/postgres/tables.sql 75 | echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );' 76 | echo COMMIT 77 | ) | ${pkgs.postgresql}/bin/psql -U mediawiki mediawiki 78 | touch /var/lib/psql-schemas/mediawiki-created 79 | fi 80 | ''; 81 | }; 82 | 83 | /* 84 | services.schemas = singleton 85 | { id = "mediawiki"; 86 | database = "mediawiki"; 87 | owner = "mediawiki"; 88 | createOwner = true; 89 | definition = pkgs.mediawiki; 90 | }; 91 | */ 92 | }; 93 | 94 | } 95 | -------------------------------------------------------------------------------- /examples/nix-homepage.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { services.httpd.enable = true; 4 | services.httpd.adminAddr = "eelco.dolstra@logicblox.com"; 5 | 6 | networking.firewall.allowedTCPPorts = [ 80 ]; 7 | 8 | # Serve the NixOS homepage. 9 | services.httpd.documentRoot = 10 | with pkgs; 11 | stdenv.mkDerivation { 12 | name = "nixos.org-homepage"; 13 | src = fetchFromGitHub { 14 | owner = "NixOS"; 15 | repo = "nixos-homepage"; 16 | rev = "2aa65af0cf4d15109bf985f4ad2f01941100f5d8"; 17 | sha256 = "1szmzfpxxsp38mxs2nzx5awbr8av6z11bnv23dvs57y8lyyrp0g2"; 18 | }; 19 | buildInputs = 20 | [ perl 21 | perlPackages.TemplateToolkit 22 | perlPackages.TemplatePluginJSONEscape 23 | perlPackages.TemplatePluginIOAll 24 | perlPackages.XMLSimple 25 | python2 26 | libxslt libxml2 imagemagick 27 | xhtml1 28 | nix 29 | ]; 30 | preBuild = 31 | '' 32 | patchShebangs . 33 | echo '[]' > nixpkgs-commits.json 34 | echo '[]' > nixpkgs-commit-stats.json 35 | touch blogs.xml 36 | echo '[]' > blogs.json 37 | cp ${} nixos/amis.nix 38 | ln -s ${nix}/share/doc/nix/manual nix/manual-raw 39 | ln -s ${config.system.build.manual.manual}/share/doc/nixos nixos/manual-raw 40 | ln -s ${(import ../release.nix {}).build.x86_64-linux}/share/doc/nixops nixops/manual-raw 41 | export NIX_STATE_DIR=$(pwd)/tmp 42 | nix-store --init 43 | touch nixpkgs/packages.json.gz nixos/options.json.gz 44 | ''; 45 | installPhase = 46 | '' 47 | rm -rf nix/manual-raw nixos/manual-raw nixops/manual-raw 48 | mkdir -p $out 49 | cp -prvd * $out/ 50 | ''; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /examples/route53.nix: -------------------------------------------------------------------------------- 1 | 2 | { 3 | machine = 4 | { resources, ... }: 5 | { imports = [ ./ec2-info.nix ./nix-homepage.nix ]; 6 | deployment.targetEnv = "ec2"; 7 | deployment.ec2.region = "us-east-1"; 8 | deployment.ec2.instanceType = "r3.large"; 9 | deployment.ec2.securityGroups = [ resources.ec2SecurityGroups.ssh-security-group ]; 10 | }; 11 | 12 | resources.ec2SecurityGroups.ssh-security-group = { 13 | region = "us-east-1"; 14 | rules = [ { 15 | fromPort = 22; 16 | toPort = 22; 17 | sourceIp = "0.0.0.0/0"; 18 | } { 19 | fromPort = 80; 20 | toPort = 80; 21 | sourceIp = "0.0.0.0/0"; 22 | }]; 23 | }; 24 | 25 | resources.route53HostedZones.hs = 26 | { name = "nixos.org."; 27 | comment = "Hosted zone for nixos.org"; 28 | }; 29 | 30 | resources.route53RecordSets = { 31 | 32 | a-record = { resources, ... }: { 33 | zoneId = resources.route53HostedZones.hs; 34 | domainName = "www.nixos.org."; 35 | ttl = 300; 36 | recordValues = [ "1.2.3.4" ]; 37 | recordType = "A"; 38 | }; 39 | 40 | mv1 = { resources, ... }: { 41 | zoneId = resources.route53HostedZones.hs; 42 | domainName = "mv.nixos.org."; 43 | recordValues = [ "1.2.3.4" ]; 44 | recordType = "A"; 45 | setIdentifier = "id1"; 46 | routingPolicy = "multivalue"; 47 | healthCheckId = resources.route53HealthChecks.my-google-check; 48 | }; 49 | 50 | mv2 = { resources, ... }: { 51 | zoneId = resources.route53HostedZones.hs; 52 | domainName = "mv.nixos.org."; 53 | ttl = 300; 54 | recordValues = [ "4.3.2.1" ]; 55 | recordType = "A"; 56 | setIdentifier = "id2"; 57 | routingPolicy = "multivalue"; 58 | healthCheckId = resources.route53HealthChecks.my-machine-check; 59 | }; 60 | 61 | weight1 = { resources, ... }: { 62 | zoneId = resources.route53HostedZones.hs; 63 | domainName = "weight.nixos.org."; 64 | weight = 10; 65 | recordValues = [ "5.4.3.2" ]; 66 | recordType = "A"; 67 | setIdentifier = "id1"; 68 | routingPolicy = "weighted"; 69 | }; 70 | 71 | weight2 = { resources, ... }: { 72 | zoneId = resources.route53HostedZones.hs; 73 | domainName = "weight.nixos.org."; 74 | weight = 20; 75 | recordValues = [ "2.3.4.5" ]; 76 | recordType = "A"; 77 | setIdentifier = "id2"; 78 | routingPolicy = "weighted"; 79 | }; 80 | }; 81 | 82 | resources.route53HealthChecks = { 83 | my-google-check = { 84 | type = "HTTPS"; 85 | fullyQualifiedDomainName = "www.google.com"; 86 | }; 87 | my-machine-check = { resources, ... }: { 88 | type = "HTTP"; 89 | ipAddress = resources.machines.machine; 90 | }; 91 | my-machine-check-with-resource-path = { resources, ... }: { 92 | type = "HTTP"; 93 | ipAddress = resources.machines.machine; 94 | resourcePath = "/nixops/"; 95 | }; 96 | calc-check = { resources, ... }: { 97 | type = "CALCULATED"; 98 | childHealthChecks = [ 99 | resources.route53HealthChecks.my-google-check 100 | resources.route53HealthChecks.my-machine-check 101 | ]; 102 | }; 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /examples/s3-bucket.nix: -------------------------------------------------------------------------------- 1 | { region ? "us-east-1" 2 | , accessKeyId ? "DOVAH..." 3 | , ... 4 | }: 5 | { 6 | resources.s3Buckets.s3-test-bucket = 7 | { 8 | inherit region accessKeyId; 9 | name = "s3-test-bucket"; 10 | versioning = "Suspended"; 11 | policy = '' 12 | { 13 | "Version": "2012-10-17", 14 | "Statement": [ 15 | { 16 | "Sid": "testing", 17 | "Effect": "Allow", 18 | "Principal": "*", 19 | "Action": "s3:GetObject", 20 | "Resource": "arn:aws:s3:::s3-test-bucket/*" 21 | } 22 | ] 23 | } 24 | ''; 25 | lifeCycle = '' 26 | { 27 | "Rules": [ 28 | { 29 | "Status": "Enabled", 30 | "Prefix": "", 31 | "Transitions": [ 32 | { 33 | "Days": 30, 34 | "StorageClass": "GLACIER" 35 | } 36 | ], 37 | "ID": "Glacier", 38 | "AbortIncompleteMultipartUpload": 39 | { 40 | "DaysAfterInitiation": 7 41 | } 42 | } 43 | ] 44 | } 45 | ''; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /examples/sns-topic.nix: -------------------------------------------------------------------------------- 1 | { account 2 | , region ? "us-east-1" 3 | , description ? "SNS topic example" 4 | , ... 5 | }: 6 | { 7 | network.description = description; 8 | 9 | resources.snsTopics.example-topic = { 10 | name = "sns-topic"; 11 | displayName = "Nixops SNS topic"; 12 | accessKeyId = account; 13 | subscriptions = [ 14 | { 15 | protocol = "email"; 16 | endpoint = "amine@chikhaoui.tn"; 17 | } 18 | ]; 19 | inherit region; 20 | }; 21 | } -------------------------------------------------------------------------------- /examples/terminal-server-ec2.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine = 3 | { imports = [ ./ec2-info.nix ]; 4 | deployment.targetEnv = "ec2"; 5 | deployment.ec2.region = "us-east-1"; 6 | deployment.ec2.instanceType = "m1.large"; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /examples/terminal-server.nix: -------------------------------------------------------------------------------- 1 | { 2 | network.description = "NixOS terminal server"; 3 | 4 | machine = 5 | { config, pkgs, modulesPath, ... }: 6 | 7 | { 8 | imports = [ "${modulesPath}/services/x11/terminal-server.nix" ]; 9 | 10 | services.xserver.desktopManager.kde4.enable = true; 11 | services.xserver.desktopManager.xfce.enable = true; 12 | 13 | environment.systemPackages = [ pkgs.glxinfo pkgs.firefoxWrapper ]; 14 | }; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /examples/trivial-ec2-ebs.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine = 3 | { imports = [ ./ec2-info.nix ]; 4 | deployment.targetEnv = "ec2"; 5 | deployment.ec2.region = "eu-west-1"; 6 | deployment.ec2.instanceType = "m1.small"; 7 | deployment.ec2.ebsBoot = true; 8 | 9 | # Mount a 1 GiB EBS volume on /data. It's created and formatted 10 | # when the machine is deployed, and destroyed when the machine 11 | # is destroyed. 12 | fileSystems."/data" = 13 | { autoFormat = true; 14 | fsType = "btrfs"; 15 | device = "/dev/xvdf"; 16 | ec2.size = 1; 17 | }; 18 | 19 | # Mount an encrypted 1 GiB volume on /secret. 20 | fileSystems."/secret" = 21 | { autoFormat = true; 22 | fsType = "ext4"; 23 | device = "/dev/mapper/xvdg"; # <-- note you need to use /dev/mapper here 24 | ec2.size = 1; 25 | ec2.encrypt = true; 26 | # You can specify a passphrase (encryption key), or let 27 | # NixOps generate one. It's stored on the root volume of 28 | # the instance, unless you set the option 29 | # ‘deployment.storeKeysOnMachine’. In that case, unattended 30 | # reboots will block until you run the command ‘nixops 31 | # send-keys’. 32 | #ec2.passphrase = "fubar"; 33 | }; 34 | 35 | # You can attach existing volumes. These are not deleted 36 | # automatically. 37 | #deployment.ec2.blockDeviceMapping."/dev/xvdh".disk = "vol-66568d4b"; 38 | 39 | # You can create volumes from snapshots. These are deleted 40 | # automatically. 41 | #deployment.ec2.blockDeviceMapping."/dev/xvdi".disk = "snap-49953c22"; 42 | 43 | # You can also specify ephemeral device mappings, but that's 44 | # rarely useful. 45 | #deployment.ec2.blockDeviceMapping."/dev/xvdd".disk = "ephemeral0"; 46 | 47 | fileSystems."/data-ssd" = 48 | { autoFormat = true; 49 | fsType = "ext4"; 50 | device = "/dev/xvdj"; 51 | ec2.size = 1; 52 | ec2.volumeType = "gp2"; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /examples/trivial-ec2.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine = 3 | { imports = [ ./ec2-info.nix ]; 4 | deployment.targetEnv = "ec2"; 5 | deployment.ec2.region = "us-east-1"; 6 | deployment.ec2.instanceType = "t2.medium"; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /examples/trivial.nix: -------------------------------------------------------------------------------- 1 | { 2 | network.description = "Trivial test network"; 3 | 4 | machine = 5 | { config, pkgs, ... }: 6 | { imports = [ ./nix-homepage.nix ]; }; 7 | } 8 | -------------------------------------------------------------------------------- /examples/vpc.nix: -------------------------------------------------------------------------------- 1 | { 2 | region ? "us-east-1" 3 | , accessKeyId 4 | , ... 5 | }: 6 | with (import {}).lib; 7 | { 8 | 9 | machine = 10 | {config, resources, pkgs, lib, ...}: 11 | { 12 | deployment.targetEnv = "ec2"; 13 | deployment.ec2.accessKeyId = accessKeyId; 14 | deployment.ec2.associatePublicIpAddress = true; 15 | deployment.ec2.region = region; 16 | deployment.ec2.instanceType = "c3.large"; 17 | deployment.ec2.subnetId = resources.vpcSubnets.subnet-b; 18 | deployment.ec2.keyPair = resources.ec2KeyPairs.keypair; 19 | deployment.ec2.securityGroups = []; 20 | deployment.ec2.securityGroupIds = [ resources.ec2SecurityGroups.sg.name ]; 21 | }; 22 | 23 | resources.ec2KeyPairs.keypair = { inherit region accessKeyId; }; 24 | 25 | resources.ec2SecurityGroups = { 26 | sg = 27 | { resources, lib, ... }: 28 | { 29 | inherit region accessKeyId; 30 | vpcId = resources.vpc.vpc-nixops; 31 | rules = [ 32 | { toPort = 22; fromPort = 22; sourceIp = "41.231.120.171/32"; } 33 | ]; 34 | }; 35 | }; 36 | 37 | resources.vpc.vpc-nixops = 38 | { 39 | inherit region accessKeyId; 40 | instanceTenancy = "default"; 41 | enableDnsSupport = true; 42 | enableDnsHostnames = true; 43 | cidrBlock = "10.0.0.0/16"; 44 | tags = { 45 | Source = "NixOps"; 46 | }; 47 | }; 48 | 49 | resources.vpcSubnets = 50 | let 51 | subnet = {cidr, zone}: 52 | { resources, ... }: 53 | { 54 | inherit region zone accessKeyId; 55 | vpcId = resources.vpc.vpc-nixops; 56 | cidrBlock = cidr; 57 | mapPublicIpOnLaunch = true; 58 | tags = { 59 | Source = "NixOps"; 60 | }; 61 | }; 62 | in 63 | { 64 | subnet-a = subnet { cidr = "10.0.0.0/19"; zone = "us-east-1a"; }; 65 | subnet-b = subnet { cidr = "10.0.32.0/19"; zone = "us-east-1b"; }; 66 | subnet-c = subnet { cidr = "10.0.64.0/19"; zone = "us-east-1c"; }; 67 | subnet-d = subnet { cidr = "10.0.96.0/19"; zone = "us-east-1d"; }; 68 | subnet-e = subnet { cidr = "10.0.128.0/19"; zone = "us-east-1e"; }; 69 | subnet-f = subnet { cidr = "10.0.160.0/19"; zone = "us-east-1f"; }; 70 | }; 71 | 72 | resources.vpcRouteTables = 73 | { 74 | route-table = 75 | { resources, ... }: 76 | { 77 | inherit region accessKeyId; 78 | vpcId = resources.vpc.vpc-nixops; 79 | }; 80 | }; 81 | 82 | resources.vpcRouteTableAssociations = 83 | let 84 | subnets = ["subnet-a" "subnet-b" "subnet-c" "subnet-d" "subnet-e" "subnet-f"]; 85 | association = subnet: 86 | { resources, ... }: 87 | { 88 | inherit region accessKeyId; 89 | subnetId = resources.vpcSubnets."${subnet}"; 90 | routeTableId = resources.vpcRouteTables.route-table; 91 | }; 92 | in 93 | (builtins.listToAttrs (map (s: nameValuePair "association-${s}" (association s) ) subnets)); 94 | 95 | resources.vpcRoutes = { 96 | igw-route = 97 | { resources, ... }: 98 | { 99 | inherit region accessKeyId; 100 | routeTableId = resources.vpcRouteTables.route-table; 101 | destinationCidrBlock = "0.0.0.0/0"; 102 | gatewayId = resources.vpcInternetGateways.igw; 103 | }; 104 | }; 105 | 106 | resources.elasticIPs.nat-eip = 107 | { 108 | inherit region accessKeyId; 109 | vpc = true; 110 | }; 111 | 112 | resources.vpcNatGateways.nat = 113 | { resources, ... }: 114 | { 115 | inherit region accessKeyId; 116 | allocationId = resources.elasticIPs.nat-eip; 117 | subnetId = resources.vpcSubnets.subnet-f; 118 | }; 119 | 120 | resources.vpcInternetGateways.igw = 121 | { resources, ... }: 122 | { 123 | inherit region accessKeyId; 124 | vpcId = resources.vpc.vpc-nixops; 125 | }; 126 | } 127 | -------------------------------------------------------------------------------- /examples/webdslorg-lb-ec2.nix: -------------------------------------------------------------------------------- 1 | let 2 | 3 | config = 4 | { deployment.targetEnv = "ec2"; 5 | deployment.ec2.controller = https://ec2.eu-west-1.amazonaws.com:443/; 6 | deployment.ec2.ami = "ami-ecb49e98"; 7 | deployment.ec2.instanceType = "m1.large"; 8 | deployment.ec2.keyPair = "gsg-keypair"; 9 | }; 10 | 11 | in 12 | 13 | { 14 | test0 = config; 15 | test1 = config; 16 | test2 = config; 17 | } 18 | -------------------------------------------------------------------------------- /examples/webdslorg-lb.nix: -------------------------------------------------------------------------------- 1 | import ./webdsldeploy/generate-network.nix { 2 | adminAddr = "foo@bar.com"; 3 | databasePassword = "admin"; 4 | 5 | applications = [ 6 | { name = "webdslorg"; 7 | src = ./webdslorg; 8 | rootapp = true; 9 | } 10 | ]; 11 | 12 | distribution = { 13 | test0 = { 14 | proxy = true; 15 | }; 16 | 17 | test1 = { 18 | tomcat = true; 19 | httpd = true; 20 | mysqlMaster = true; 21 | }; 22 | 23 | test2 = { 24 | tomcat = true; 25 | httpd = true; 26 | mysqlSlave = 2; 27 | }; 28 | }; 29 | } // 30 | { 31 | /*test3 = {pkgs, ...}: 32 | { 33 | services = { 34 | xserver = { 35 | enable = true; 36 | 37 | displayManager = { 38 | slim.enable = false; 39 | auto.enable = true; 40 | }; 41 | 42 | windowManager = { 43 | default = "icewm"; 44 | icewm = { 45 | enable = true; 46 | }; 47 | }; 48 | 49 | desktopManager.default = "none"; 50 | }; 51 | }; 52 | 53 | environment = { 54 | systemPackages = [ 55 | pkgs.mc 56 | pkgs.subversion 57 | pkgs.lynx 58 | pkgs.firefox 59 | ]; 60 | }; 61 | };*/ 62 | } 63 | -------------------------------------------------------------------------------- /maintainers/dump-route53-hosted-zone.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env nix-shell 2 | #! nix-shell -i python -p pythonPackages.boto3 -I channel:nixos-unstable 3 | 4 | import json 5 | import os 6 | import boto3 7 | import sys 8 | 9 | session = boto3.session.Session() 10 | client = session.client("route53") 11 | 12 | domain = sys.argv[1] 13 | if not domain.endswith("."): 14 | domain += "." 15 | 16 | zone_id = client.list_hosted_zones_by_name(DNSName=domain)["HostedZones"][0]["Id"] 17 | records = client.list_resource_record_sets(HostedZoneId=zone_id)["ResourceRecordSets"] 18 | 19 | by_hostname = {} 20 | for record in records: 21 | name = record["Name"] 22 | if name not in by_hostname: 23 | by_hostname[name] = [] 24 | by_hostname[name].append(record) 25 | 26 | count = {k: sum(1 for x in v if x["Type"] == "A") for (k, v) in by_hostname.items()} 27 | 28 | 29 | def print_record(record): 30 | if record["Type"] in ("NS", "SOA"): 31 | return 32 | 33 | res = record["Name"][: -(len(domain) + 1)] 34 | mv = False 35 | if count[record["Name"]] > 1: 36 | mv = True 37 | res = res + "-" + record["SetIdentifier"] 38 | if res == "": 39 | res = record["Type"] + "-record" 40 | 41 | print((' "{0}" = {{ resources, ... }}: {{'.format(res))) 42 | print(" zoneId = resources.route53HostedZones.hs;") 43 | print(" inherit accessKeyId;") 44 | print((' routingPolicy = "{}";'.format("multivalue" if mv else "simple"))) 45 | print((' domainName = "{}";'.format(record["Name"]))) 46 | if "SetIdentifier" in record: 47 | print((' setIdentifier = "{}";'.format(record["SetIdentifier"]))) 48 | if "TTL" in record: 49 | print((" ttl = {};".format(record["TTL"]))) 50 | 51 | if "ResourceRecords" in record: 52 | print(" recordValues = [") 53 | for v in record["ResourceRecords"]: 54 | print((' "{}"'.format(v["Value"]))) 55 | print(" ];") 56 | 57 | if "AliasTarget" in record: 58 | print((' aliasDNSName = "{}";'.format(record["AliasTarget"]["DNSName"]))) 59 | print( 60 | ( 61 | " aliasEvaluateTargetHealth = {};".format( 62 | record["AliasTarget"]["EvaluateTargetHealth"] 63 | ) 64 | ) 65 | ) 66 | print( 67 | ( 68 | ' aliasHostedZoneId = "{}";'.format( 69 | record["AliasTarget"]["HostedZoneId"] 70 | ) 71 | ) 72 | ) 73 | 74 | print((' recordType = "{}";'.format(record["Type"]))) 75 | print(" };") 76 | 77 | 78 | print( 79 | ( 80 | """ 81 | {{ accessKeyId ? "nixos-tests" }}: 82 | {{ 83 | resources.route53HostedZones.hs = 84 | {{ name = "{}"; 85 | comment = "Hosted zone for nixos.org"; 86 | inherit accessKeyId; 87 | }}; 88 | """.format( 89 | domain 90 | ) 91 | ) 92 | ) 93 | print(" resources.route53RecordSets = {") 94 | for record in records: 95 | print_record(record) 96 | print(" };") 97 | print("}") 98 | -------------------------------------------------------------------------------- /nixops_aws/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NixOS/nixops-aws/d173b2f14ec767d782ceab45fb22b32fe3b5a1f7/nixops_aws/__init__.py -------------------------------------------------------------------------------- /nixops_aws/backends/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NixOS/nixops-aws/d173b2f14ec767d782ceab45fb22b32fe3b5a1f7/nixops_aws/backends/__init__.py -------------------------------------------------------------------------------- /nixops_aws/backends/options.py: -------------------------------------------------------------------------------- 1 | from nixops.backends import MachineOptions 2 | from typing import Optional 3 | from nixops.resources import ResourceOptions 4 | from typing import Sequence 5 | from typing import Mapping 6 | from typing_extensions import Literal 7 | from typing import Union 8 | 9 | 10 | class Route53Options(ResourceOptions): 11 | accessKeyId: str 12 | hostName: str 13 | private: bool 14 | ttl: int 15 | usePublicDNSName: bool 16 | 17 | 18 | class DiskOptions(ResourceOptions): 19 | size: int 20 | iops: Optional[int] 21 | volumeType: Union[ 22 | Literal["standard"], 23 | Literal["io1"], 24 | Literal["io2"], 25 | Literal["gp2"], 26 | Literal["gp3"], 27 | Literal["st1"], 28 | Literal["sc1"], 29 | ] 30 | disk: str 31 | fsType: str 32 | deleteOnTermination: bool 33 | encrypt: bool 34 | encryptionType: Union[ 35 | Literal["luks"], Literal["ebs"], 36 | ] 37 | cipher: str 38 | keySize: int 39 | passphrase: str 40 | 41 | 42 | class FilesystemsOptions(DiskOptions): 43 | pass 44 | 45 | 46 | class BlockdevicemappingOptions(DiskOptions): 47 | pass 48 | 49 | 50 | class Ec2Options(ResourceOptions): 51 | accessKeyId: str 52 | ami: str 53 | associatePublicIpAddress: bool 54 | blockDeviceMapping: Mapping[str, BlockdevicemappingOptions] 55 | ebsBoot: bool 56 | ebsInitialRootDiskSize: int 57 | ebsOptimized: bool 58 | elasticIPv4: str 59 | instanceId: str 60 | instanceProfile: str 61 | instanceType: str 62 | keyPair: str 63 | physicalProperties: Mapping[str, Union[int, str, bool]] 64 | placementGroup: str 65 | privateKey: str 66 | region: str 67 | securityGroupIds: Sequence[str] 68 | securityGroups: Sequence[str] 69 | sourceDestCheck: bool 70 | spotInstanceInterruptionBehavior: Union[ 71 | Literal["terminate"], Literal["stop"], Literal["hibernate"] 72 | ] 73 | spotInstancePrice: int 74 | spotInstanceRequestType: Union[Literal["one-time"], Literal["persistent"]] 75 | spotInstanceTimeout: int 76 | subnetId: str 77 | tags: Mapping[str, str] 78 | tenancy: Union[Literal["default"], Literal["dedicated"], Literal["host"]] 79 | usePrivateIpAddress: bool 80 | zone: str 81 | fileSystems: Optional[Mapping[str, FilesystemsOptions]] 82 | 83 | 84 | class EC2MachineOptions(MachineOptions): 85 | ec2: Ec2Options 86 | route53: Route53Options 87 | -------------------------------------------------------------------------------- /nixops_aws/nix/aws-data-lifecycle-manager.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | 10 | dlmName = mkOption { 11 | default = "nixops-${uuid}-${name}"; 12 | type = types.str; 13 | description = "data lifecycle manager name."; 14 | }; 15 | 16 | policyId = mkOption { 17 | default = ""; 18 | type = types.str; 19 | description = "The identifier of the lifecycle policy. This is set by NixOps"; 20 | }; 21 | 22 | description = mkOption { 23 | default = "lifecycle policy created by nixops."; 24 | type = types.str; 25 | description = "A description of the lifecycle policy. "; 26 | }; 27 | 28 | executionRole = mkOption { 29 | default = "AWSDataLifecycleManagerDefaultRole"; 30 | type = types.str; 31 | description = "IAM role used to run the operations specified by the lifecycle policy."; 32 | }; 33 | 34 | resourceTypes = mkOption { 35 | default = "instance" ; 36 | type = types.enum [ "instance" "volume" ]; 37 | description = "The resource type."; 38 | }; 39 | 40 | targetTags = mkOption { 41 | default = { }; 42 | example = { foo = "bar"; xyzzy = "bla"; }; 43 | type = types.attrsOf types.str; 44 | description = '' 45 | The single tag that identifies targeted resources for this policy. 46 | ''; 47 | }; 48 | 49 | excludeBootVolume = mkOption { 50 | default = true; 51 | type = types.bool; 52 | description = '' 53 | When executing an EBS Snapshot Management – Instance policy, 54 | execute all CreateSnapshots calls with the excludeBootVolume 55 | set to the supplied field. Defaults to false. Only valid for 56 | EBS Snapshot Management – Instance policies. 57 | ''; 58 | }; 59 | 60 | copyTags = mkOption { 61 | default = true; 62 | type = types.bool; 63 | description = '' 64 | Copy all user-defined tags on a source volume to snapshots 65 | of the volume created by this policy. 66 | ''; 67 | }; 68 | 69 | tagsToAdd = mkOption { 70 | default = { }; 71 | example = { foo = "bar"; xyzzy = "bla"; }; 72 | type = types.attrsOf types.str; 73 | description = '' 74 | The tags to apply to policy-created resources. 75 | These user-defined tags are in addition to the 76 | AWS-added lifecycle tags. 77 | ''; 78 | }; 79 | 80 | ruleInterval = mkOption { 81 | default = 12; 82 | type = types.enum [ 2 3 4 6 8 12 24 ]; 83 | description = '' 84 | The interval between snapshots. The supported 85 | values are 2, 3, 4, 6, 8, 12, and 24. 86 | ''; 87 | }; 88 | 89 | ruleIntervalUnit = mkOption { 90 | default = "hours"; 91 | type = types.enum [ "hours" ]; 92 | description = "The interval unit."; 93 | }; 94 | 95 | ruleTime = mkOption { 96 | default = "09:00"; 97 | type = types.str; 98 | description = '' 99 | The time, in UTC, to start the operation. 100 | The supported format is hh:mm. The operation occurs 101 | within a one-hour window following the specified time. 102 | ''; 103 | }; 104 | 105 | retainRule = mkOption { 106 | default = 1000; 107 | type = types.int; 108 | description = '' 109 | The number of snapshots to keep for each volume, up to a maximum of 1000. 110 | ''; 111 | }; 112 | }; 113 | 114 | config._type = "aws-data-lifecycle-manager"; 115 | } -------------------------------------------------------------------------------- /nixops_aws/nix/aws-vpn-connection-route.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | name = mkOption { 10 | default = "nixops-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the VPN connection route."; 13 | }; 14 | 15 | vpnConnectionId = mkOption { 16 | type = types.either types.str (resource "aws-vpn-connection"); 17 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 18 | description = '' 19 | The ID of the VPN connection. 20 | ''; 21 | }; 22 | 23 | destinationCidrBlock = mkOption { 24 | default = null; 25 | type = types.nullOr types.str; 26 | description = '' 27 | The IPv4 CIDR address block used for the destination match. 28 | ''; 29 | }; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /nixops_aws/nix/aws-vpn-connection.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the AWS VPN connection."; 15 | }; 16 | 17 | vpnGatewayId = mkOption { 18 | type = types.either types.str (resource "aws-vpn-gateway"); 19 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 20 | description = '' 21 | The ID of the VPN gateway. 22 | ''; 23 | }; 24 | 25 | customerGatewayId = mkOption { 26 | type = types.either types.str (resource "vpc-customer-gateway"); 27 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 28 | description = '' 29 | The ID of the customer gateway. 30 | ''; 31 | }; 32 | 33 | staticRoutesOnly = mkOption { 34 | default = false; 35 | type = types.bool; 36 | description = '' 37 | Indicates whether the VPN connection uses static routes only. 38 | Static routes must be used for devices that don't support BGP. 39 | ''; 40 | }; 41 | 42 | } // import ./common-ec2-options.nix { inherit lib; }; 43 | 44 | config._type = "aws-vpn-connection"; 45 | } 46 | -------------------------------------------------------------------------------- /nixops_aws/nix/aws-vpn-gateway.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the AWS VPN gateway."; 15 | }; 16 | 17 | zone = mkOption { 18 | # NOTE: We're making this required in NixOps but the api can handle 19 | # choosing the zone. Making this required will prevent having 20 | # the diff engine trigger the zone handler in each deploy. 21 | type = types.str; 22 | description = "AWS availability zone."; 23 | }; 24 | 25 | vpcId = mkOption { 26 | type = types.either types.str (resource "vpc"); 27 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 28 | description = '' 29 | The ID of the VPC where the VPN gateway will be attached. 30 | ''; 31 | }; 32 | } // import ./common-ec2-options.nix { inherit lib; }; 33 | 34 | config._type = "aws-vpn-gateway"; 35 | } 36 | -------------------------------------------------------------------------------- /nixops_aws/nix/cloudwatch-log-group.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the cloudwatch log group."; 13 | }; 14 | 15 | accessKeyId = mkOption { 16 | type = types.str; 17 | default = ""; 18 | description = "The AWS Access Key ID."; 19 | }; 20 | 21 | region = mkOption { 22 | type = types.str; 23 | description = "AWS region."; 24 | }; 25 | 26 | retentionInDays = mkOption { 27 | default = null; 28 | type = types.nullOr types.int; 29 | description = "How long to store log data in a log group"; 30 | }; 31 | 32 | arn = mkOption { 33 | default = ""; 34 | type = types.str; 35 | description = "Amazon Resource Name (ARN) of the cloudwatch log group. This is set by NixOps."; 36 | }; 37 | 38 | }; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /nixops_aws/nix/cloudwatch-log-stream.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the cloudwatch log stream."; 13 | }; 14 | 15 | accessKeyId = mkOption { 16 | type = types.str; 17 | default = ""; 18 | description = "The AWS Access Key ID."; 19 | }; 20 | 21 | region = mkOption { 22 | type = types.str; 23 | description = "AWS region."; 24 | }; 25 | 26 | logGroupName = mkOption { 27 | type = types.str; 28 | description = "The name of the log group under which the log stream is to be created."; 29 | }; 30 | 31 | arn = mkOption { 32 | default = ""; 33 | type = types.str; 34 | description = "Amazon Resource Name (ARN) of the cloudwatch log stream. This is set by NixOps."; 35 | }; 36 | 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /nixops_aws/nix/common-ebs-options.nix: -------------------------------------------------------------------------------- 1 | # Options shared between the EBS resource type and the 2 | # deployment.ec2.blockDeviceMapping/fileSystems.*.ec2 options in EC2 3 | # instances. 4 | 5 | { config, lib, ... }: 6 | 7 | with lib; 8 | 9 | { 10 | 11 | options = { 12 | 13 | size = mkOption { 14 | example = 100; 15 | type = types.int; 16 | description = '' 17 | Volume size (in gigabytes). This may be left unset if you are 18 | creating the volume from a snapshot, in which case the size of 19 | the volume will be equal to the size of the snapshot. 20 | However, you can set a size larger than the snapshot, allowing 21 | the volume to be larger than the snapshot from which it is 22 | created. 23 | ''; 24 | }; 25 | 26 | iops = mkOption { 27 | default = null; 28 | type = types.nullOr types.int; 29 | description = '' 30 | The provisioned IOPS you want to associate with this EBS volume. 31 | ''; 32 | }; 33 | 34 | volumeType = mkOption { 35 | default = if config.iops == null then "standard" else "io1"; # io2? 36 | type = types.enum [ "standard" "io1" "io2" "gp2" "gp3" "st1" "sc1" ]; 37 | description = '' 38 | The volume type for the EBS volume, which must be one of 39 | "standard" (a magnetic volume), 40 | "io1" (a provisioned IOPS SSD volume), 41 | "io2" (an improved provisioned IOPS SSD volume) or 42 | "gp2" (a general purpose SSD volume). 43 | "gp3" (a general purpose SSD volume). 44 | "st1" (a throughput optimized HDD volume). 45 | "sc1" (a cold HDD volume). 46 | ''; 47 | }; 48 | 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /nixops_aws/nix/common-ec2-auth-options.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | options = { 7 | accessKeyId = mkOption { 8 | default = ""; 9 | type = types.str; 10 | description = '' 11 | The AWS Access Key ID. 12 | ''; 13 | }; 14 | region = mkOption { 15 | type = types.str; 16 | description = '' 17 | AWS region. 18 | ''; 19 | }; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /nixops_aws/nix/common-ec2-options.nix: -------------------------------------------------------------------------------- 1 | { lib }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | tags = mkOption { 8 | default = { }; 9 | example = { foo = "bar"; xyzzy = "bla"; }; 10 | type = types.attrsOf types.str; 11 | description = '' 12 | Tags assigned to the instance. Each tag name can be at most 13 | 128 characters, and each tag value can be at most 256 14 | characters. There can be at most 10 tags. 15 | ''; 16 | }; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /nixops_aws/nix/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config_exporters = { optionalAttrs, ... }: [ 3 | (config: { ec2 = optionalAttrs (config.deployment.targetEnv == "ec2") config.deployment.ec2; }) 4 | (config: { route53 = config.deployment.route53; }) 5 | ]; 6 | options = [ 7 | ./ec2.nix 8 | ./route53.nix 9 | ]; 10 | resources = { evalResources, zipAttrs, resourcesByType, ... }: { 11 | snsTopics = evalResources ./sns-topic.nix (zipAttrs resourcesByType.snsTopics or []); 12 | sqsQueues = evalResources ./sqs-queue.nix (zipAttrs resourcesByType.sqsQueues or []); 13 | ec2KeyPairs = evalResources ./ec2-keypair.nix (zipAttrs resourcesByType.ec2KeyPairs or []); 14 | s3Buckets = evalResources ./s3-bucket.nix (zipAttrs resourcesByType.s3Buckets or []); 15 | iamRoles = evalResources ./iam-role.nix (zipAttrs resourcesByType.iamRoles or []); 16 | ec2SecurityGroups = evalResources ./ec2-security-group.nix (zipAttrs resourcesByType.ec2SecurityGroups or []); 17 | ec2PlacementGroups = evalResources ./ec2-placement-group.nix (zipAttrs resourcesByType.ec2PlacementGroups or []); 18 | ebsVolumes = evalResources ./ebs-volume.nix (zipAttrs resourcesByType.ebsVolumes or []); 19 | elasticIPs = evalResources ./elastic-ip.nix (zipAttrs resourcesByType.elasticIPs or []); 20 | rdsDbInstances = evalResources ./ec2-rds-dbinstance.nix (zipAttrs resourcesByType.rdsDbInstances or []); 21 | rdsSubnetGroups = evalResources ./rds-subnet-group.nix (zipAttrs resourcesByType.rdsSubnetGroups or []); 22 | rdsDbSecurityGroups = evalResources ./ec2-rds-dbsecurity-group.nix (zipAttrs resourcesByType.rdsDbSecurityGroups or []); 23 | route53RecordSets = evalResources ./route53-recordset.nix (zipAttrs resourcesByType.route53RecordSets or []); 24 | elasticFileSystems = evalResources ./elastic-file-system.nix (zipAttrs resourcesByType.elasticFileSystems or []); 25 | elasticFileSystemMountTargets = evalResources ./elastic-file-system-mount-target.nix (zipAttrs resourcesByType.elasticFileSystemMountTargets or []); 26 | cloudwatchLogGroups = evalResources ./cloudwatch-log-group.nix (zipAttrs resourcesByType.cloudwatchLogGroups or []); 27 | cloudwatchLogStreams = evalResources ./cloudwatch-log-stream.nix (zipAttrs resourcesByType.cloudwatchLogStreams or []); 28 | cloudwatchMetricAlarms = evalResources ./cloudwatch-metric-alarm.nix (zipAttrs resourcesByType.cloudwatchMetricAlarms or []); 29 | route53HostedZones = evalResources ./route53-hosted-zone.nix (zipAttrs resourcesByType.route53HostedZones or []); 30 | route53HealthChecks = evalResources ./route53-health-check.nix (zipAttrs resourcesByType.route53HealthChecks or []); 31 | vpc = evalResources ./vpc.nix (zipAttrs resourcesByType.vpc or []); 32 | vpcSubnets = evalResources ./vpc-subnet.nix (zipAttrs resourcesByType.vpcSubnets or []); 33 | vpcInternetGateways = evalResources ./vpc-internet-gateway.nix (zipAttrs resourcesByType.vpcInternetGateways or []); 34 | vpcEgressOnlyInternetGateways = evalResources ./vpc-egress-only-internet-gateway.nix (zipAttrs resourcesByType.vpcEgressOnlyInternetGateways or []); 35 | vpcDhcpOptions = evalResources ./vpc-dhcp-options.nix (zipAttrs resourcesByType.vpcDhcpOptions or []); 36 | vpcNetworkAcls = evalResources ./vpc-network-acl.nix (zipAttrs resourcesByType.vpcNetworkAcls or []); 37 | vpcNatGateways = evalResources ./vpc-nat-gateway.nix (zipAttrs resourcesByType.vpcNatGateways or []); 38 | vpcNetworkInterfaces = evalResources ./vpc-network-interface.nix (zipAttrs resourcesByType.vpcNetworkInterfaces or []); 39 | vpcNetworkInterfaceAttachments = evalResources ./vpc-network-interface-attachment.nix (zipAttrs resourcesByType.vpcNetworkInterfaceAttachments or []); 40 | vpcRouteTables = evalResources ./vpc-route-table.nix (zipAttrs resourcesByType.vpcRouteTables or []); 41 | vpcRouteTableAssociations = evalResources ./vpc-route-table-association.nix (zipAttrs resourcesByType.vpcRouteTableAssociations or []); 42 | vpcRoutes = evalResources ./vpc-route.nix (zipAttrs resourcesByType.vpcRoutes or []); 43 | vpcCustomerGateways = evalResources ./vpc-customer-gateway.nix (zipAttrs resourcesByType.vpcCustomerGateways or []); 44 | vpcEndpoints = evalResources ./vpc-endpoint.nix (zipAttrs resourcesByType.vpcEndpoints or []); 45 | awsVPNGateways = evalResources ./aws-vpn-gateway.nix (zipAttrs resourcesByType.awsVPNGateways or []); 46 | awsVPNConnections = evalResources ./aws-vpn-connection.nix (zipAttrs resourcesByType.awsVPNConnections or []); 47 | awsVPNConnectionRoutes = evalResources ./aws-vpn-connection-route.nix (zipAttrs resourcesByType.awsVPNConnectionRoutes or []); 48 | awsDataLifecycleManager = evalResources ./aws-data-lifecycle-manager.nix (zipAttrs resourcesByType.awsDataLifecycleManager or []); 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /nixops_aws/nix/ebs-volume.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | imports = [ ./common-ebs-options.nix ]; 8 | 9 | options = { 10 | 11 | region = mkOption { 12 | example = "us-east-1"; 13 | type = types.str; 14 | description = "AWS region."; 15 | }; 16 | 17 | zone = mkOption { 18 | example = "us-east-1c"; 19 | type = types.str; 20 | description = '' 21 | The EC2 availability zone in which the volume should be 22 | created. 23 | ''; 24 | }; 25 | 26 | accessKeyId = mkOption { 27 | type = types.str; 28 | default = ""; 29 | description = "The AWS Access Key ID."; 30 | }; 31 | 32 | volumeId = mkOption { 33 | default = ""; 34 | example = "vol-abc123"; 35 | type = types.str; 36 | description = '' 37 | The volume id to be imported into the NixOps ebs-volume resource. 38 | ''; 39 | }; 40 | 41 | snapshot = mkOption { 42 | default = ""; 43 | example = "snap-1cbda474"; 44 | type = types.str; 45 | description = '' 46 | The snapshot ID from which this volume will be created. If 47 | not specified, an empty volume is created. Changing the 48 | snapshot ID has no effect if the volume already exists. 49 | ''; 50 | }; 51 | 52 | } // import ./common-ec2-options.nix { inherit lib; }; 53 | 54 | config = { 55 | _type = "ebs-volume"; 56 | size = mkIf (config.snapshot != "" || config.volumeId != "") (mkDefault 0); 57 | }; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /nixops_aws/nix/ec2-keypair.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the EC2 key pair."; 13 | }; 14 | 15 | region = mkOption { 16 | type = types.str; 17 | description = "AWS region."; 18 | }; 19 | 20 | accessKeyId = mkOption { 21 | default = ""; 22 | type = types.str; 23 | description = "The AWS Access Key ID."; 24 | }; 25 | 26 | }; 27 | 28 | config._type = "ec2-keypair"; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /nixops_aws/nix/ec2-placement-group.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the placement group."; 13 | }; 14 | 15 | strategy = mkOption { 16 | default = "cluster"; 17 | type = types.str; 18 | description = "The placement strategy of the new placement group. Currently, the only acceptable value is “cluster”."; 19 | }; 20 | 21 | region = mkOption { 22 | type = types.str; 23 | description = "AWS region."; 24 | }; 25 | 26 | accessKeyId = mkOption { 27 | default = ""; 28 | type = types.str; 29 | description = "The AWS Access Key ID."; 30 | }; 31 | }; 32 | 33 | config._type = "ec2-placement-group"; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /nixops_aws/nix/ec2-rds-dbinstance.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | with import ./lib.nix lib; 5 | 6 | { 7 | 8 | options = { 9 | 10 | id = mkOption { 11 | type = types.str; 12 | default = "nixops-${uuid}-${name}"; 13 | description = "Identifier for RDS database instance"; 14 | }; 15 | 16 | region = mkOption { 17 | type = types.str; 18 | description = "Amazon RDS region."; 19 | }; 20 | 21 | multiAZ = mkOption { 22 | default = false; 23 | type = types.bool; 24 | description = '' 25 | If True, specifies the DB Instance will be deployed in multiple availability zones. 26 | ''; 27 | }; 28 | 29 | accessKeyId = mkOption { 30 | default = ""; 31 | type = types.str; 32 | description = "The AWS Access Key ID."; 33 | }; 34 | 35 | allocatedStorage = mkOption { 36 | type = types.int; 37 | description = "Allocated storage in GB"; 38 | }; 39 | 40 | instanceClass = mkOption { 41 | type = types.str; 42 | example = "db.m3.xlarge"; 43 | description = '' 44 | RDS instance class. See 46 | for more information. 47 | ''; 48 | }; 49 | 50 | masterUsername = mkOption { 51 | type = types.str; 52 | example = "sa"; 53 | description = "Master username for authentication to database instance."; 54 | }; 55 | 56 | masterPassword = mkOption { 57 | type = types.str; 58 | description = "Password for master user."; 59 | }; 60 | 61 | port = mkOption { 62 | type = types.int; 63 | description = "Port for database instance connections."; 64 | }; 65 | 66 | engine = mkOption { 67 | type = types.str; 68 | description = ''Database engine. See ) 4 | listToAttrs 5 | ; 6 | response = builtins.fromJSON (builtins.readFile ./instance-types.json); 7 | 8 | isSupported = x: { 9 | "default" = true; 10 | "supported" = true; 11 | "required" = true; 12 | "unsupported" = false; 13 | }.${x}; 14 | 15 | isNVMeSupported = x: { 16 | "supported" = true; 17 | "required" = true; # z1d 18 | "unsupported" = false; 19 | }.${x}; 20 | 21 | getPlatforms = x: map toPlatform x.ProcessorInfo.SupportedArchitectures; 22 | 23 | # Some machine types are suitable for darwin (and probably only usable with that) 24 | # This makes the SupperedArchitectures more of a platform list than an architecture 25 | # list. 26 | toPlatform = arch: { 27 | "arm64" = { cpu = "aarch64"; }; 28 | "x86_64" = { cpu = "x86_64"; }; 29 | 30 | # wonky but we don't support as old as true i386, which it probably isn't 31 | "i386" = { cpu = "i686"; }; 32 | "x86_64_mac" = { cpu = "x86_64"; os = "darwin"; }; 33 | "arm64_mac" = { cpu = "aarch64"; os = "darwin"; }; 34 | }.${arch} or (throw "It seems that ec2 has a new CPU architecture: ${arch}"); 35 | 36 | summarize = instanceType: { 37 | cores = instanceType.VCpuInfo.DefaultVCpus; 38 | memory = instanceType.MemoryInfo.SizeInMiB; 39 | allowsEbsOptimized = isSupported instanceType.EbsInfo.EbsOptimizedSupport; 40 | supportsNVMe = isNVMeSupported instanceType.EbsInfo.NvmeSupport; 41 | platforms = getPlatforms instanceType; 42 | }; 43 | 44 | 45 | in listToAttrs (map (v: { name = v.InstanceType; value = summarize v; }) response.InstanceTypes) 46 | -------------------------------------------------------------------------------- /nixops_aws/nix/generate-ec2-properties.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import json 3 | 4 | # FIXME: AWS support adviced against the use of this index file for anything other than pricing 5 | # This file also does not provide a way to confidently check if an instance use nvme or not 6 | # and there is currently no API that can provide such information. So manual fixes are needed 7 | # after running this script. 8 | # A feature request is in place to provide an endpoint that can cover our need. 9 | # curl -O https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/index.json 10 | with open("index.json") as f: 11 | data = json.load(f) 12 | 13 | instanceTypes = {} 14 | for p in data["products"].keys(): 15 | product = data["products"][p]["attributes"] 16 | if ( 17 | "operatingSystem" in product 18 | and product["operatingSystem"] in ("NA", "Linux") 19 | and "tenancy" in product 20 | and product["tenancy"] == "Shared" 21 | and product["location"] == "US East (N. Virginia)" 22 | ): 23 | ebsOptimized = "ebsOptimized" in product 24 | instanceType = product["instanceType"] 25 | cores = product["vcpu"] 26 | memory = int(float(product["memory"].replace(",", "").split(" ")[0]) * 1024) 27 | if instanceType in instanceTypes and not ebsOptimized: 28 | continue 29 | instanceTypes[product["instanceType"]] = ( 30 | ' "' 31 | + instanceType 32 | + '" = { cores = ' 33 | + cores 34 | + "; memory = " 35 | + str(memory) 36 | + "; allowsEbsOptimized = " 37 | + ("true" if ebsOptimized else "false") 38 | + "; supportsNVMe = " 39 | + "false" 40 | + ";};" 41 | ) 42 | 43 | print("{") 44 | for instanceType in sorted(instanceTypes.keys()): 45 | print(instanceTypes[instanceType]) 46 | print("}") 47 | -------------------------------------------------------------------------------- /nixops_aws/nix/generate-ec2-properties.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #!nix-shell -p awscli 3 | #!nix-shell -p nixpkgs-fmt 4 | #!nix-shell -I nixpkgs=channel:nixos-unstable 5 | #!nix-shell -i bash 6 | set -euo pipefail 7 | 8 | if test -z "${NO_FETCH:-}"; then 9 | aws ec2 describe-instance-types --region us-east-1 >instance-types.json 10 | fi 11 | 12 | nix-instantiate ec2-properties.nix --eval --strict | sed -e 's/; "/;\n "/' | nixpkgs-fmt >ec2-properties-orig-sorted.nix 13 | 14 | nix-instantiate --strict --eval ./generate-ec2-properties.nix \ 15 | | sed -e 's/; "/;\n "/' \ 16 | | nixpkgs-fmt \ 17 | > ec2-properties-generated.nix 18 | 19 | diff --color=always ec2-properties-orig-sorted.nix ec2-properties-generated.nix 20 | -------------------------------------------------------------------------------- /nixops_aws/nix/iam-role.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the IAM role."; 13 | }; 14 | 15 | accessKeyId = mkOption { 16 | type = types.str; 17 | default = ""; 18 | description = "The AWS Access Key ID."; 19 | }; 20 | 21 | policy = mkOption { 22 | type = types.str; 23 | description = "The IAM policy definition (in JSON format)."; 24 | }; 25 | 26 | assumeRolePolicy = mkOption { 27 | type = types.str; 28 | description = "The IAM AssumeRole policy definition (in JSON format). Empty string (default) uses the existing Assume Role Policy."; 29 | default = ""; 30 | }; 31 | 32 | } // import ./common-ec2-options.nix { inherit lib; }; 33 | } 34 | -------------------------------------------------------------------------------- /nixops_aws/nix/lib.nix: -------------------------------------------------------------------------------- 1 | lib: 2 | 3 | { 4 | 5 | resource = type: lib.mkOptionType { 6 | name = "resource of type ‘${type}’"; 7 | check = x: x._type or "" == type; 8 | merge = lib.mergeOneOption; 9 | }; 10 | 11 | shorten_uuid = uuid: lib.replaceChars ["-"] [""] uuid; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /nixops_aws/nix/rds-subnet-group.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | with import ./lib.nix lib; 6 | 7 | { 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the RDS subnet group."; 15 | }; 16 | 17 | description = mkOption { 18 | default = "NixOps-provisioned subnet group ${name}"; 19 | type = types.str; 20 | description = "Informational description of the subnet group."; 21 | }; 22 | 23 | accessKeyId = mkOption { 24 | default = ""; 25 | type = types.str; 26 | description = "The AWS Access Key ID."; 27 | }; 28 | 29 | region = mkOption { 30 | example = "us-east-1"; 31 | type = types.str; 32 | description = '' 33 | AWS region in which the instance is to be deployed. 34 | ''; 35 | }; 36 | 37 | subnetIds = mkOption { 38 | default = []; 39 | example = [ "subnet-00000000" ]; 40 | type = types.listOf (types.either types.str (resource "vpc-subnet")); 41 | apply = map (x: if builtins.isString x then x else "res-" + x._name + "." + x._type); 42 | description = '' 43 | The subnets inside a VPC to launch the databases in. 44 | ''; 45 | }; 46 | }; 47 | 48 | config._type = "rds-subnet-group"; 49 | } 50 | -------------------------------------------------------------------------------- /nixops_aws/nix/route53-hosted-zone.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "nixops-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the recordset."; 13 | }; 14 | 15 | accessKeyId = mkOption { 16 | type = types.str; 17 | default = ""; 18 | description = '' 19 | The AWS Access Key ID. If left empty, it defaults to the 20 | contents of the environment variables 21 | EC2_ACCESS_KEY or 22 | AWS_ACCESS_KEY_ID (in that order). The 23 | corresponding Secret Access Key is not specified in the 24 | deployment model, but looked up in the file 25 | ~/.ec2-keys, which should specify, on 26 | each line, an Access Key ID followed by the corresponding 27 | Secret Access Key. If the lookup was unsuccessful it is continued 28 | in the standard AWS tools ~/.aws/credentials file. 29 | If it does not appear in these files, the 30 | environment variables 31 | EC2_SECRET_KEY or 32 | AWS_SECRET_ACCESS_KEY are used. 33 | ''; 34 | }; 35 | 36 | comment = mkOption { 37 | default = ""; 38 | type = types.str; 39 | description = '' 40 | Comments that you want to include about the hosted zone. 41 | ''; 42 | }; 43 | 44 | privateZone = mkOption { 45 | default = false; 46 | type = types.bool; 47 | description = '' 48 | Whether this is a private hosted zone. 49 | ''; 50 | }; 51 | 52 | associatedVPCs = mkOption { 53 | type = with types; listOf (submodule { 54 | options = { 55 | vpcId = mkOption { 56 | type = str; 57 | description = "The ID of an Amazon VPC."; 58 | }; 59 | region = mkOption { 60 | type = str; 61 | description = "The region in which you created an Amazon VPC."; 62 | }; 63 | }; 64 | }); 65 | default = []; 66 | description = "VPCs"; 67 | }; 68 | 69 | delegationSet = mkOption { 70 | default = []; 71 | internal = true; 72 | type = types.listOf types.str; 73 | description = '' 74 | List of nameserves in the delegation set after creation. Set by nixops. 75 | ''; 76 | }; 77 | 78 | }; 79 | 80 | config = { 81 | _type = "route53-hosted-zone"; 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /nixops_aws/nix/route53-recordset.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | with (import ./lib.nix lib); 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "nixops-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the recordset."; 13 | }; 14 | 15 | accessKeyId = mkOption { 16 | type = types.str; 17 | default = ""; 18 | description = '' 19 | The AWS Access Key ID. If left empty, it defaults to the 20 | contents of the environment variables 21 | EC2_ACCESS_KEY or 22 | AWS_ACCESS_KEY_ID (in that order). The 23 | corresponding Secret Access Key is not specified in the 24 | deployment model, but looked up in the file 25 | ~/.ec2-keys, which should specify, on 26 | each line, an Access Key ID followed by the corresponding 27 | Secret Access Key. If the lookup was unsuccessful it is continued 28 | in the standard AWS tools ~/.aws/credentials file. 29 | If it does not appear in these files, the 30 | environment variables 31 | EC2_SECRET_KEY or 32 | AWS_SECRET_ACCESS_KEY are used. 33 | ''; 34 | }; 35 | 36 | zoneId = mkOption { 37 | type = types.nullOr (types.either types.str (resource "route53-hosted-zone")); 38 | apply = x: if (builtins.isString x) || ( x == null) then x else "res-" + x._name; 39 | default = null; 40 | description = "The DNS hosted zone id. If null, the zoneName will be used to look up the zoneID"; 41 | }; 42 | 43 | zoneName = mkOption { 44 | type = types.nullOr types.str; 45 | default = null; 46 | description = "The DNS name of the hosted zone"; 47 | }; 48 | 49 | domainName = mkOption { 50 | type = types.str; 51 | description = "The DNS name to bind."; 52 | }; 53 | 54 | ttl = mkOption { 55 | type = types.int; 56 | default = 300; 57 | example = 300; 58 | 59 | description = '' 60 | The time to live (TTL) for the A record created for the 61 | specified DNS hostname. 62 | ''; 63 | }; 64 | 65 | routingPolicy = mkOption { 66 | type = types.enum [ "simple" "weighted" "multivalue" ]; 67 | default = "simple"; 68 | 69 | description = '' 70 | DNS record type 71 | ''; 72 | }; 73 | 74 | recordType = mkOption { 75 | type = types.enum [ "A" "AAAA" "TXT" "CNAME" "MX" "NAPT" "PTR" "SRV" "SPF" ]; 76 | default = "A"; 77 | 78 | description = '' 79 | DNS record type 80 | ''; 81 | }; 82 | 83 | recordValues = mkOption { 84 | type = types.listOf (types.oneOf [ 85 | types.str 86 | (resource "machine") 87 | (resource "elastic-ip") 88 | ]); 89 | 90 | apply = l: map (x: if (builtins.isString x) || ( x == null) then x else "res-" + x._name) l; 91 | 92 | description = '' 93 | The value of the DNS record 94 | (e.g. IP address in case of an A or AAA record type, 95 | or a DNS name in case of a CNAME record type) 96 | ''; 97 | }; 98 | 99 | setIdentifier = mkOption { 100 | type = types.str; 101 | default = ""; 102 | description = '' 103 | A unique identifier that differentiates among multiple 104 | resource record sets that have the same combination of 105 | DNS name and type. 106 | ''; 107 | }; 108 | 109 | weight = mkOption { 110 | type = types.int; 111 | default = 0; 112 | description = '' 113 | Among resource record sets that have the same combination 114 | of DNS name and type, a value that determines what portion 115 | of traffic for the current resource record set is routed 116 | to the associated location. When value is 0, weighted 117 | routing policy is not used. 118 | ''; 119 | }; 120 | 121 | healthCheckId = mkOption { 122 | type = types.either types.str (resource "route53-health-check"); 123 | default = ""; 124 | apply = x: if (builtins.isString x) || ( x == null) then x else "res-" + x. _name; 125 | description = '' 126 | Optional ID of an Amazon Route 53 health check. 127 | ''; 128 | }; 129 | }; 130 | } 131 | -------------------------------------------------------------------------------- /nixops_aws/nix/route53.nix: -------------------------------------------------------------------------------- 1 | # Configuration specific to the Amazon Route 53 service. 2 | 3 | { config, lib, ... }: 4 | 5 | with lib; 6 | 7 | { 8 | ###### interface 9 | 10 | options = { 11 | 12 | deployment.route53.accessKeyId = mkOption { 13 | default = ""; 14 | example = "AKIABOGUSACCESSKEY"; 15 | type = types.str; 16 | description = '' 17 | The AWS Access Key ID. If left empty, it defaults to the 18 | contents of the environment variables 19 | EC2_ACCESS_KEY or 20 | AWS_ACCESS_KEY_ID (in that order). The 21 | corresponding Secret Access Key is not specified in the 22 | deployment model, but looked up in the file 23 | ~/.ec2-keys, which should specify, on 24 | each line, an Access Key ID followed by the corresponding 25 | Secret Access Key. If the lookup was unsuccessful it is continued 26 | in the standard AWS tools ~/.aws/credentials file. 27 | If it does not appear in these files, the 28 | environment variables 29 | EC2_SECRET_KEY or 30 | AWS_SECRET_ACCESS_KEY are used. 31 | ''; 32 | }; 33 | 34 | # FIXME: hostName is a misnomer; rename to dnsName or something like that. 35 | deployment.route53.hostName = mkOption { 36 | default = ""; 37 | example = "test.x.logicblox.com"; 38 | type = types.str; 39 | description = '' 40 | The DNS hostname to bind the public IP address to. 41 | ''; 42 | }; 43 | 44 | deployment.route53.ttl = mkOption { 45 | default = 300; 46 | example = 300; 47 | type = types.int; 48 | description = '' 49 | The time to live (TTL) for the A record created for the 50 | specified DNS hostname. 51 | ''; 52 | }; 53 | 54 | deployment.route53.usePublicDNSName = mkOption { 55 | default = false; 56 | type = types.bool; 57 | description = '' 58 | Whether to create a CNAME record with the instance's public DNS name. 59 | This will resolve inside AWS to a private IP and outside AWS to 60 | the public IP. 61 | ''; 62 | }; 63 | 64 | deployment.route53.private = mkOption { 65 | default = false; 66 | type = types.bool; 67 | description = '' 68 | Whether to create an A record with the instance's private address. 69 | 70 | Make sure to use this on a Private Hosted DNS zones only, because it will 71 | appear as if the host is down at best, but may cause erroneous requests to 72 | be routed to hosts on your clients' local networks. 73 | ''; 74 | }; 75 | 76 | }; 77 | 78 | 79 | ###### implementation 80 | 81 | config = mkIf (config.deployment.targetEnv == "ec2") {}; 82 | 83 | } 84 | -------------------------------------------------------------------------------- /nixops_aws/nix/s3-bucket.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the S3 bucket."; 13 | }; 14 | 15 | region = mkOption { 16 | type = types.str; 17 | description = "Amazon S3 region."; 18 | }; 19 | 20 | accessKeyId = mkOption { 21 | type = types.str; 22 | default = ""; 23 | description = "The AWS Access Key ID."; 24 | }; 25 | 26 | arn = mkOption { 27 | default = "arn:aws:s3:::${config.name}"; 28 | type = types.str; 29 | description = "Amazon Resource Name (ARN) of the S3 bucket. This is set by NixOps."; 30 | }; 31 | 32 | policy = mkOption { 33 | type = types.str; 34 | default = ""; 35 | description = "The JSON Policy string to apply to the bucket."; 36 | }; 37 | 38 | lifeCycle = mkOption { 39 | type = types.str; 40 | default = ""; 41 | description = "The JSON lifecycle management string to apply to the bucket."; 42 | }; 43 | 44 | versioning = mkOption { 45 | default = "Suspended"; 46 | type = types.enum [ "Suspended" "Enabled" ]; 47 | description = "Whether to enable S3 versioning or not. Valid values are 'Enabled' or 'Suspended'"; 48 | }; 49 | 50 | persistOnDestroy = mkOption { 51 | default = false; 52 | type = types.bool; 53 | description = '' 54 | If set to true nixops destroy won't delete the bucket 55 | on destroy. 56 | ''; 57 | }; 58 | 59 | website.enabled = mkOption { 60 | type = types.bool; 61 | default = false; 62 | description = "Whether to serve the S3 bucket as public website."; 63 | }; 64 | 65 | website.suffix = mkOption { 66 | type = types.str; 67 | default = "index.html"; 68 | description = "A suffix that is appended to a request that is for a directory on the website endpoint."; 69 | }; 70 | 71 | website.errorDocument = mkOption { 72 | type = types.str; 73 | default = ""; 74 | description = "The S3 key to serve when response is an error."; 75 | }; 76 | 77 | 78 | }; 79 | 80 | } 81 | -------------------------------------------------------------------------------- /nixops_aws/nix/sns-topic.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the SNS topic."; 13 | }; 14 | 15 | region = mkOption { 16 | type = types.str; 17 | description = "AWS region."; 18 | }; 19 | 20 | accessKeyId = mkOption { 21 | type = types.str; 22 | default = ""; 23 | description = "The AWS Access Key ID."; 24 | }; 25 | 26 | displayName = mkOption { 27 | default = null; 28 | type = types.nullOr (types.str); 29 | description = "Display name of the topic"; 30 | }; 31 | 32 | policy = mkOption { 33 | default = ""; 34 | type = types.str; 35 | description = "Policy to apply to the SNS topic."; 36 | }; 37 | 38 | subscriptions = mkOption { 39 | description = "List of subscriptions to apply to the topic."; 40 | default = []; 41 | type = with types; listOf (submodule { 42 | options = { 43 | protocol = mkOption { 44 | default = null; 45 | description = "The protocol to use."; 46 | type = types.str; 47 | }; 48 | endpoint = mkOption { 49 | default = null; 50 | description = "The endpoint to send data to."; 51 | type = types.str; 52 | }; 53 | }; 54 | }); 55 | }; 56 | 57 | arn = mkOption { 58 | default = ""; 59 | type = types.str; 60 | description = "Amazon Resource Name (ARN) of the SNS topic. This is set by NixOps."; 61 | }; 62 | 63 | }; 64 | 65 | config = { 66 | _type = "sns-topic"; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /nixops_aws/nix/sqs-queue.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "charon-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the SQS queue."; 13 | }; 14 | 15 | region = mkOption { 16 | type = types.str; 17 | description = "AWS region."; 18 | }; 19 | 20 | accessKeyId = mkOption { 21 | type = types.str; 22 | default = ""; 23 | description = "The AWS Access Key ID."; 24 | }; 25 | 26 | visibilityTimeout = mkOption { 27 | default = 30; 28 | type = types.int; 29 | description = 30 | '' 31 | The time interval in seconds after a message has been 32 | received until it becomes visible again. 33 | ''; 34 | }; 35 | 36 | url = mkOption { 37 | default = ""; # FIXME: don't set a default 38 | type = types.str; 39 | description = "URL of the queue. This is set by NixOps."; 40 | }; 41 | 42 | arn = mkOption { 43 | default = ""; # FIXME: don't set a default 44 | type = types.str; 45 | description = "Amazon Resource Name (ARN) of the queue. This is set by NixOps."; 46 | }; 47 | 48 | }; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-customer-gateway.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | { 5 | imports = [ ./common-ec2-auth-options.nix ]; 6 | 7 | options = { 8 | 9 | name = mkOption { 10 | default = "nixops-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the VPC customer gateway."; 13 | }; 14 | 15 | bgpAsn = mkOption { 16 | type = types.int; 17 | description = '' 18 | For devices that support BGP, the customer gateway's BGP ASN. 19 | ''; 20 | }; 21 | 22 | publicIp = mkOption { 23 | type = types.str; 24 | description = '' 25 | The Internet-routable IP address for the customer gateway's outside interface. 26 | The address must be static. 27 | ''; 28 | }; 29 | 30 | type = mkOption { 31 | type = types.str; 32 | description = '' 33 | The type of VPN connection that this customer gateway supports (ipsec.1 ). 34 | ''; 35 | }; 36 | 37 | } // import ./common-ec2-options.nix { inherit lib; }; 38 | 39 | config._type = "vpc-customer-gateway"; 40 | } 41 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-dhcp-options.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | 10 | name = mkOption { 11 | default = "nixops-${uuid}-${name}"; 12 | type = types.str; 13 | description = "Name of the DHCP options set."; 14 | }; 15 | 16 | vpcId = mkOption { 17 | type = types.either types.str (resource "vpc"); 18 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 19 | description = '' 20 | The ID of the VPC used to associate the DHCP options to. 21 | ''; 22 | }; 23 | 24 | domainNameServers = mkOption { 25 | default = null; 26 | type = types.nullOr (types.listOf types.str); 27 | description = '' 28 | The IP addresses of up to 4 domain name servers, or AmazonProvidedDNS. 29 | ''; 30 | }; 31 | 32 | domainName = mkOption { 33 | default = null; 34 | type = types.nullOr types.str; 35 | description = '' 36 | If you're using AmazonProvidedDNS in us-east-1, specify ec2.internal. 37 | If you're using another region specify region.compute.internal (e.g 38 | ap-northeast-1.compute.internal). Otherwise specify a domain name e.g 39 | MyCompany.com. This value is used to complete unqualified DNS hostnames. 40 | ''; 41 | }; 42 | 43 | ntpServers = mkOption { 44 | default = null; 45 | type = types.nullOr (types.listOf types.str); 46 | description = '' 47 | The IP addresses of up to 4 Network Time Protocol (NTP) servers. 48 | ''; 49 | }; 50 | 51 | netbiosNameServers = mkOption { 52 | default = null; 53 | type = types.nullOr (types.listOf types.str); 54 | description = '' 55 | The IP addresses of up to 4 NetBIOS name servers. 56 | ''; 57 | }; 58 | 59 | netbiosNodeType = mkOption { 60 | default = null; 61 | type = types.nullOr types.int; 62 | description = '' 63 | The NetBIOS node type (1,2,4 or 8). 64 | ''; 65 | }; 66 | 67 | } // import ./common-ec2-options.nix { inherit lib; }; 68 | } 69 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-egress-only-internet-gateway.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the VPC egress only internet gateway."; 15 | }; 16 | 17 | vpcId = mkOption { 18 | type = types.either types.str (resource "vpc"); 19 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 20 | description = '' 21 | The ID of the VPC where the internet gateway will be created 22 | ''; 23 | }; 24 | }; 25 | 26 | config._type = "vpc-egress-only-internet-gateway"; 27 | } 28 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-endpoint.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the VPC endpoint."; 15 | }; 16 | 17 | vpcId = mkOption { 18 | type = types.either types.str (resource "vpc"); 19 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 20 | description = '' 21 | The ID of the VPC where the endpoint will be created. 22 | ''; 23 | }; 24 | 25 | policy = mkOption { 26 | default = null; 27 | type = types.nullOr types.str; 28 | description = '' 29 | A policy to attach to the endpoint that controls access to the service. 30 | ''; 31 | }; 32 | 33 | routeTableIds = mkOption { 34 | default = []; 35 | type = types.listOf (types.either types.str (resource "vpc-route-table")); 36 | apply = map (x: if builtins.isString x then x else "res-" + x._name + "." + x._type + "." + "routeTableId"); 37 | description = '' 38 | One or more route table IDs. 39 | ''; 40 | }; 41 | 42 | serviceName = mkOption { 43 | type = types.str; 44 | description = '' 45 | The AWS service name, in the form com.amazonaws.region.service. 46 | ''; 47 | }; 48 | 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-internet-gateway.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the VPC internet gateway."; 15 | }; 16 | 17 | vpcId = mkOption { 18 | type = types.either types.str (resource "vpc"); 19 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 20 | description = '' 21 | The ID of the VPC where the internet gateway will be created 22 | ''; 23 | }; 24 | } // import ./common-ec2-options.nix { inherit lib; }; 25 | 26 | config._type = "vpc-internet-gateway"; 27 | } 28 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-nat-gateway.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the VPC NAT gateway."; 15 | }; 16 | 17 | allocationId = mkOption { 18 | type = types.either types.str (resource "elastic-ip"); 19 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type + ".allocation_id"; 20 | description = '' 21 | The allocation ID of the elastic IP address. 22 | ''; 23 | }; 24 | 25 | subnetId = mkOption { 26 | type = types.either types.str (resource "vpc-subnet"); 27 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 28 | description = '' 29 | The ID of the VPC subnet where the NAT gateway will be created 30 | ''; 31 | }; 32 | }; 33 | 34 | config._type = "vpc-nat-gateway"; 35 | } 36 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-network-acl.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | let 6 | networkAclEntry = { 7 | options = { 8 | ruleNumber = mkOption { 9 | type = types.int; 10 | description = '' 11 | The rule number of the entry. ACL entries are processed in asceding order by rule 12 | number. 13 | ''; 14 | }; 15 | protocol = mkOption { 16 | type = types.str; 17 | description = '' 18 | The protocol to match. If using the -1 'all' protocol, you must specify a from and 19 | to port of 0. 20 | ''; 21 | }; 22 | ruleAction = mkOption { 23 | type = types.str; 24 | description = '' 25 | The action to take. Can be either "allow" or "deny". 26 | ''; 27 | }; 28 | egress = mkOption { 29 | type = types.bool; 30 | description = '' 31 | Indicates whether this is an egress rule (rule is applied to traffic leaving the subnet). 32 | ''; 33 | }; 34 | cidrBlock = mkOption { 35 | default = null; 36 | type = types.nullOr types.str; 37 | description = '' 38 | The IPv4 network range to allow or deny, in CIDR notation. 39 | ''; 40 | }; 41 | ipv6CidrBlock = mkOption { 42 | default = null; 43 | type = types.nullOr types.str; 44 | description = '' 45 | The IPv6 network range to allow or deny, in CIDR notation. 46 | ''; 47 | }; 48 | icmpCode = mkOption { 49 | default = null; 50 | type = types.nullOr types.int; 51 | description = '' 52 | The ICMP type code to be used. 53 | ''; 54 | }; 55 | icmpType = mkOption { 56 | default = null; 57 | type = types.nullOr types.int; 58 | description = '' 59 | The ICMP type to be used. 60 | ''; 61 | }; 62 | fromPort = mkOption { 63 | default = null; 64 | type = types.nullOr types.int; 65 | description = '' 66 | The first port in the range. 67 | ''; 68 | }; 69 | toPort = mkOption { 70 | default = null; 71 | type = types.nullOr types.int; 72 | description = '' 73 | The last port in the range. 74 | ''; 75 | }; 76 | }; 77 | }; 78 | in 79 | { 80 | imports = [ ./common-ec2-auth-options.nix ]; 81 | 82 | options = { 83 | 84 | name = mkOption { 85 | default = "nixops-${uuid}-${name}"; 86 | type = types.str; 87 | description = "Name of the DHCP options set."; 88 | }; 89 | 90 | vpcId = mkOption { 91 | type = types.either types.str (resource "vpc"); 92 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 93 | description = '' 94 | The Id of the associated VPC. 95 | ''; 96 | }; 97 | 98 | subnetIds = mkOption { 99 | default = []; 100 | type = types.listOf (types.either types.str (resource "vpc-subnet")); 101 | apply = map (x: if builtins.isString x then x else "res-" + x._name + "." + x._type + "." + "subnetId"); 102 | description = '' 103 | A list of subnet IDs to apply to the ACL to. 104 | ''; 105 | }; 106 | 107 | entries = mkOption { 108 | description = "The network ACL entries"; 109 | default = []; 110 | type = with types; listOf (submodule networkAclEntry); 111 | }; 112 | 113 | networkAclId = mkOption { 114 | default = ""; 115 | type = types.str; 116 | description = "The network ACL id generated from AWS. This is set by NixOps"; 117 | }; 118 | } // import ./common-ec2-options.nix { inherit lib; }; 119 | 120 | config._type = "vpc-network-acl"; 121 | } 122 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-network-interface-attachment.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | let 6 | machine= mkOptionType { 7 | name = "EC2 machine"; 8 | check = x: x ? ec2; 9 | merge = mergeOneOption; 10 | }; 11 | in 12 | { 13 | imports = [ ./common-ec2-auth-options.nix ]; 14 | 15 | options = { 16 | 17 | name = mkOption { 18 | default = "nixops-${uuid}-${name}"; 19 | type = types.str; 20 | description = "Name of the VPC network interface attachment."; 21 | }; 22 | 23 | networkInterfaceId = mkOption { 24 | type = types.either types.str (resource "vpc-network-interface"); 25 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 26 | description = '' 27 | ENI ID to attach to. 28 | ''; 29 | }; 30 | 31 | instanceId = mkOption { 32 | type = types.either types.str machine; 33 | apply = x: if builtins.isString x then x else "res-" + x._name + ".ec2." + "vm_id"; 34 | description = '' 35 | ID of the instance to attach to. 36 | ''; 37 | }; 38 | 39 | deviceIndex = mkOption { 40 | type = types.int; 41 | description = '' 42 | The index of the device for the network interface attachment. 43 | ''; 44 | }; 45 | 46 | }; 47 | 48 | config = { 49 | _type = "vpc-network-interface-attachment"; 50 | }; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-network-interface.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | 10 | name = mkOption { 11 | default = "nixops-${uuid}-${name}"; 12 | type = types.str; 13 | description = "Name of the VPC network interface."; 14 | }; 15 | 16 | subnetId = mkOption { 17 | type = types.either types.str (resource "vpc-subnet"); 18 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 19 | description = '' 20 | Subnet Id to create the ENI in. 21 | ''; 22 | }; 23 | 24 | description = mkOption { 25 | default = ""; 26 | type = types.str; 27 | description = '' 28 | A description for the network interface. 29 | ''; 30 | }; 31 | 32 | securityGroups = mkOption { 33 | default = null; 34 | type = types.listOf (types.either types.str (resource "ec2-security-group")); 35 | apply = map (x: if builtins.isString x then x else "res-" + x._name + "." + x._type + "." + "security_group_id"); 36 | description = '' 37 | The IDs of one or more security groups. 38 | ''; 39 | }; 40 | 41 | primaryPrivateIpAddress = mkOption { 42 | default = null; 43 | type = types.nullOr types.str; 44 | description = '' 45 | The primary private IPv4 address of the network interface. If you don't 46 | specify an IPv4 address, Amazon EC2 selects one for you from the subnet's 47 | IPv4 CIDR range. 48 | ''; 49 | }; 50 | 51 | privateIpAddresses = mkOption { 52 | default = []; 53 | type = types.listOf types.str; 54 | description = '' 55 | One or more secondary private IPv4 addresses. 56 | ''; 57 | }; 58 | 59 | secondaryPrivateIpAddressCount = mkOption { 60 | default = null; 61 | type = types.nullOr types.int; 62 | description = '' 63 | The number of secondary private IPv4 addresses to assign to a network interface. 64 | When you specify a number of secondary IPv4 addresses, Amazon EC2 selects these 65 | IP addresses within the subnet's IPv4 CIDR range. 66 | You can't specify this option and specify privateIpAddresses in the same time. 67 | ''; 68 | }; 69 | 70 | sourceDestCheck = mkOption { 71 | default = true; 72 | type = types.bool; 73 | description = '' 74 | Indicates whether source/destination checking is enabled. 75 | Default value is true. 76 | ''; 77 | }; 78 | 79 | } // import ./common-ec2-options.nix { inherit lib; }; 80 | 81 | config = { 82 | _type = "vpc-network-interface"; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-route-table-association.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | name = mkOption { 10 | default = "nixops-${uuid}-${name}"; 11 | type = types.str; 12 | description = "Name of the VPC route table association."; 13 | }; 14 | 15 | subnetId = mkOption { 16 | type = types.either types.str (resource "vpc-subnet"); 17 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 18 | description = '' 19 | The ID of the VPC subnet where the route table will be associated 20 | ''; 21 | }; 22 | 23 | routeTableId = mkOption { 24 | type = types.either types.str (resource "vpc-route-table"); 25 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 26 | description = '' 27 | The ID of the VPC route table 28 | ''; 29 | }; 30 | }; 31 | 32 | config._type = "vpc-route-table-association"; 33 | } 34 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-route-table.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | 10 | name = mkOption { 11 | default = "nixops-${uuid}-${name}"; 12 | type = types.str; 13 | description = "Name of the VPC route table."; 14 | }; 15 | 16 | vpcId = mkOption { 17 | type = types.either types.str (resource "vpc"); 18 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 19 | description = '' 20 | The ID of the VPC where the route table will be created 21 | ''; 22 | }; 23 | 24 | propagatingVgws = mkOption { 25 | default = []; 26 | type = types.listOf (types.either types.str (resource "aws-vpn-gateway")); 27 | apply = map (x: if builtins.isString x then x else "res-" + x._name + "." + x._type + ".vpnGatewayId"); 28 | description = '' 29 | A list of VPN gateways for propagation. 30 | ''; 31 | }; 32 | 33 | } // import ./common-ec2-options.nix { inherit lib; }; 34 | 35 | config._type = "vpc-route-table"; 36 | } 37 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-route.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | let 6 | machine= mkOptionType { 7 | name = "EC2 machine"; 8 | check = x: x ? ec2; 9 | merge = mergeOneOption; 10 | }; 11 | in 12 | { 13 | imports = [ ./common-ec2-auth-options.nix ]; 14 | 15 | options = { 16 | name = mkOption { 17 | default = "nixops-${uuid}-${name}"; 18 | type = types.str; 19 | description = "Name of the VPC route."; 20 | }; 21 | 22 | routeTableId = mkOption { 23 | type = types.either types.str (resource "vpc-route-table"); 24 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 25 | description = '' 26 | The ID of the VPC route table 27 | ''; 28 | }; 29 | 30 | destinationCidrBlock = mkOption { 31 | default = null; 32 | type = types.nullOr types.str; 33 | description = '' 34 | The IPv4 CIDR address block used for the destination match. 35 | ''; 36 | }; 37 | 38 | destinationIpv6CidrBlock = mkOption { 39 | default = null; 40 | type = types.nullOr types.str; 41 | description = '' 42 | The IPv6 CIDR block used for the destination match. 43 | ''; 44 | }; 45 | 46 | gatewayId = mkOption { 47 | default = null; 48 | type = types.nullOr (types.either types.str (resource "vpc-internet-gateway")); 49 | apply = x: if (builtins.isString x || builtins.isNull x) then x else "res-" + x._name + "." + x._type + ".internetGatewayId"; 50 | description = '' 51 | The ID of an Internet gateway or virtual private gateway attached to your VPC. 52 | ''; 53 | }; 54 | 55 | egressOnlyInternetGatewayId = mkOption { 56 | default = null; 57 | type = types.nullOr (types.either types.str (resource "vpc-egress-only-internet-gateway")); 58 | apply = x: if (builtins.isString x || builtins.isNull x) then x else "res-" + x._name + "." + x._type + ".egressOnlyInternetGatewayId"; 59 | description = '' 60 | [IPv6 traffic only] The ID of an egress-only Internet gateway. 61 | ''; 62 | }; 63 | 64 | instanceId = mkOption { 65 | default = null; 66 | type = types.nullOr (types.either types.str machine); 67 | apply = x: if (builtins.isString x || builtins.isNull x) then x else "res-" + x._name + ".ec2." + "vm_id"; 68 | description = '' 69 | The ID of a NAT instance in your VPC. The operation fails if you specify an 70 | instance ID unless exactly one network interface is attached. 71 | ''; 72 | }; 73 | 74 | natGatewayId = mkOption { 75 | default = null; 76 | type = types.nullOr (types.either types.str (resource "vpc-nat-gateway")); 77 | apply = x: if (builtins.isString x || builtins.isNull x) then x else "res-" + x._name + "." + x._type; 78 | description = '' 79 | The ID of a NAT gateway. 80 | ''; 81 | }; 82 | 83 | networkInterfaceId = mkOption { 84 | default = null; 85 | type = types.nullOr (types.either types.str (resource "vpc-network-interface")); 86 | apply = x: if (builtins.isString x || builtins.isNull x) then x else "res-" + x._name + "." + x._type; 87 | description = '' 88 | The ID of a network interface. 89 | ''; 90 | }; 91 | 92 | #TODO add VPC peering connection 93 | 94 | }; 95 | 96 | config._type = "vpc-route"; 97 | } 98 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc-subnet.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with import ./lib.nix lib; 4 | with lib; 5 | 6 | { 7 | imports = [ ./common-ec2-auth-options.nix ]; 8 | 9 | options = { 10 | 11 | name = mkOption { 12 | default = "nixops-${uuid}-${name}"; 13 | type = types.str; 14 | description = "Name of the subnet VPC."; 15 | }; 16 | 17 | vpcId = mkOption { 18 | type = types.either types.str (resource "vpc"); 19 | apply = x: if builtins.isString x then x else "res-" + x._name + "." + x._type; 20 | description = '' 21 | The ID of the VPC where the subnet will be created 22 | ''; 23 | }; 24 | 25 | cidrBlock = mkOption { 26 | type = types.str; 27 | description = "The CIDR block for the VPC subnet"; 28 | }; 29 | 30 | ipv6CidrBlock = mkOption { 31 | default = null; 32 | type = types.nullOr types.str; 33 | description = '' 34 | The IPv6 network range for the subnet, in CIDR notation. 35 | The subnet size must use a /64 prefix length. 36 | ''; 37 | }; 38 | 39 | zone = mkOption { 40 | type = types.str; 41 | description = '' 42 | The availability zone for the VPC subnet. 43 | By default AWS selects one for you. 44 | ''; 45 | }; 46 | 47 | mapPublicIpOnLaunch = mkOption { 48 | default = false; 49 | type = types.bool; 50 | description = '' 51 | Indicates whether instances launched into the subnet should be assigned 52 | a public IP in launch. Default is false. 53 | ''; 54 | }; 55 | 56 | subnetId = mkOption { 57 | default = ""; 58 | type = types.str; 59 | description = "The VPC subnet id generated from AWS. This is set by NixOps"; 60 | }; 61 | 62 | } // import ./common-ec2-options.nix { inherit lib; } ; 63 | 64 | config._type = "vpc-subnet"; 65 | 66 | } 67 | -------------------------------------------------------------------------------- /nixops_aws/nix/vpc.nix: -------------------------------------------------------------------------------- 1 | { config, lib, uuid, name, ... }: 2 | 3 | with lib; 4 | 5 | { 6 | imports = [ ./common-ec2-auth-options.nix ]; 7 | 8 | options = { 9 | 10 | name = mkOption { 11 | default = "nixops-${uuid}-${name}"; 12 | type = types.str; 13 | description = "Name of the VPC."; 14 | }; 15 | 16 | cidrBlock = mkOption { 17 | type = types.str; 18 | description = "The CIDR block for the VPC"; 19 | }; 20 | 21 | instanceTenancy = mkOption { 22 | default = "default"; 23 | type = types.str; 24 | description = '' 25 | The supported tenancy options for instances launched 26 | into the VPC. Valid values are "default" and "dedicated". 27 | ''; 28 | }; 29 | 30 | enableDnsSupport = mkOption { 31 | default = false; 32 | type = types.bool; 33 | description = '' 34 | Specifies whether the DNS server provided by Amazon is enabled for the VPC. 35 | ''; 36 | }; 37 | 38 | enableDnsHostnames = mkOption { 39 | default = false; 40 | type = types.bool; 41 | description = '' 42 | Specifies whether DNS hostnames are provided for the instances launched in this VPC. 43 | You can only set this attribute to true if EnableDnsSupport is also true. 44 | ''; 45 | }; 46 | 47 | enableClassicLink = mkOption { 48 | default = false; 49 | type = types.bool; 50 | description = '' 51 | Enables a VPC for ClassicLink. You can then link EC2-Classic instances to your 52 | ClassicLink-enabled VPC to allow communication over private IP addresses. 53 | You cannot enable your VPC for ClassicLink if any of your VPC’s route tables 54 | have existing routes for address ranges within the 10.0.0.0/8 IP address range 55 | , excluding local routes for VPCs in the 10.0.0.0/16 and 10.1.0.0/16 IP address ranges. 56 | ''; 57 | }; 58 | 59 | amazonProvidedIpv6CidrBlock = mkOption { 60 | default = false; 61 | type = types.bool; 62 | description = '' 63 | Requests an Amazon-provided IPv6 CIDR block with a /56 prefix length for the VPC. 64 | You cannot specify the range of IP addresses, or the size of the CIDR block. 65 | ''; 66 | }; 67 | 68 | vpcId = mkOption { 69 | default = ""; 70 | type = types.str; 71 | description = "The VPC id generated from AWS. This is set by NixOps"; 72 | }; 73 | 74 | } // import ./common-ec2-options.nix { inherit lib; }; 75 | 76 | config._type = "vpc"; 77 | } 78 | -------------------------------------------------------------------------------- /nixops_aws/plugin.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import nixops.plugins 3 | from nixops.plugins import Plugin 4 | 5 | 6 | class NixopsAWSPlugin(Plugin): 7 | @staticmethod 8 | def nixexprs(): 9 | return [os.path.dirname(os.path.abspath(__file__)) + "/nix"] 10 | 11 | @staticmethod 12 | def load(): 13 | return [ 14 | "nixops_aws.resources", 15 | "nixops_aws.backends.ec2", 16 | "nixops_aws.resources.ec2_keypair", 17 | ] 18 | 19 | 20 | @nixops.plugins.hookimpl 21 | def plugin(): 22 | return NixopsAWSPlugin() 23 | -------------------------------------------------------------------------------- /nixops_aws/resources/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ( 2 | "aws_vpn_connection", 3 | "aws_vpn_connection_route", 4 | "aws_vpn_gateway", 5 | "cloudwatch_log_group", 6 | "cloudwatch_log_stream", 7 | "cloudwatch_metric_alarm", 8 | "ebs_volume", 9 | "ec2_common", 10 | "ec2_keypair", 11 | "ec2_placement_group", 12 | "ec2_rds_dbinstance", 13 | "ec2_rds_dbsecurity_group", 14 | "ec2_security_group", 15 | "efs_common", 16 | "elastic_file_system", 17 | "elastic_file_system_mount_target", 18 | "elastic_ip", 19 | "iam_role", 20 | "route53_health_check", 21 | "route53_hosted_zone", 22 | "route53_recordset", 23 | "s3_bucket", 24 | "sns_topic", 25 | "sqs_queue", 26 | "vpc", 27 | "vpc_customer_gateway", 28 | "vpc_dhcp_options", 29 | "vpc_egress_only_internet_gateway", 30 | "vpc_endpoint", 31 | "vpc_internet_gateway", 32 | "vpc_nat_gateway", 33 | "vpc_network_acl", 34 | "vpc_network_interface", 35 | "vpc_network_interface_attachment", 36 | "vpc_route", 37 | "vpc_route_table", 38 | "vpc_route_table_association", 39 | "vpc_subnet", 40 | "aws_data_lifecycle_manager", 41 | ) 42 | 43 | from . import aws_vpn_connection 44 | from . import aws_vpn_connection_route 45 | from . import aws_vpn_gateway 46 | from . import cloudwatch_log_group 47 | from . import cloudwatch_log_stream 48 | from . import cloudwatch_metric_alarm 49 | from . import ebs_volume 50 | from . import ec2_common 51 | from . import ec2_keypair 52 | from . import ec2_placement_group 53 | from . import ec2_rds_dbinstance 54 | from . import ec2_rds_dbsecurity_group 55 | from . import ec2_security_group 56 | from . import efs_common 57 | from . import elastic_file_system 58 | from . import elastic_file_system_mount_target 59 | from . import elastic_ip 60 | from . import iam_role 61 | from . import route53_health_check 62 | from . import route53_hosted_zone 63 | from . import route53_recordset 64 | from . import s3_bucket 65 | from . import sns_topic 66 | from . import sqs_queue 67 | from . import vpc 68 | from . import vpc_customer_gateway 69 | from . import vpc_dhcp_options 70 | from . import vpc_egress_only_internet_gateway 71 | from . import vpc_endpoint 72 | from . import vpc_internet_gateway 73 | from . import vpc_nat_gateway 74 | from . import vpc_network_acl 75 | from . import vpc_network_interface 76 | from . import vpc_network_interface_attachment 77 | from . import vpc_route 78 | from . import vpc_route_table 79 | from . import vpc_route_table_association 80 | from . import vpc_subnet 81 | from . import aws_data_lifecycle_manager 82 | -------------------------------------------------------------------------------- /nixops_aws/resources/aws_vpn_connection_route.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import botocore 4 | 5 | import nixops.util 6 | import nixops.resources 7 | from nixops_aws.resources.ec2_common import EC2CommonState 8 | from . import aws_vpn_connection 9 | from nixops.diff import Handler 10 | from nixops.state import StateDict 11 | 12 | from .aws_vpn_connection import AWSVPNConnectionState 13 | 14 | from .types.aws_vpn_connection_route import AwsVpnConnectionRouteOptions 15 | 16 | 17 | class AWSVPNConnectionRouteDefinition(nixops.resources.ResourceDefinition): 18 | """Definition of a VPN connection route""" 19 | 20 | config: AwsVpnConnectionRouteOptions 21 | 22 | @classmethod 23 | def get_type(cls): 24 | return "aws-vpn-connection-route" 25 | 26 | @classmethod 27 | def get_resource_type(cls): 28 | return "awsVPNConnectionRoutes" 29 | 30 | def show_type(self): 31 | return "{0}".format(self.get_type()) 32 | 33 | 34 | class AWSVPNConnectionRouteState( 35 | nixops.resources.DiffEngineResourceState, EC2CommonState 36 | ): 37 | definition_type = AWSVPNConnectionRouteDefinition 38 | 39 | """State of a VPN connection route""" 40 | state = nixops.util.attr_property( 41 | "state", nixops.resources.DiffEngineResourceState.MISSING, int 42 | ) 43 | access_key_id = nixops.util.attr_property("accessKeyId", None) 44 | _reserved_keys = EC2CommonState.COMMON_EC2_RESERVED 45 | 46 | @classmethod 47 | def get_type(cls): 48 | return "aws-vpn-connection-route" 49 | 50 | def __init__(self, depl, name, id): 51 | nixops.resources.DiffEngineResourceState.__init__(self, depl, name, id) 52 | self._state = StateDict(depl, id) 53 | self.region = self._state.get("region", None) 54 | self.handle_create_vpn_route = Handler( 55 | ["region", "vpnConnectionId", "destinationCidrBlock"], 56 | handle=self.realize_create_vpn_route, 57 | ) 58 | 59 | def show_type(self): 60 | s = super(AWSVPNConnectionRouteState, self).show_type() 61 | if self.region: 62 | s = "{0} [{1}]".format(s, self.region) 63 | return s 64 | 65 | @property 66 | def resource_id(self): 67 | return self._state.get("destinationCidrBlock", None) 68 | 69 | def prefix_definition(self, attr): 70 | return {("resources", "awsVPNConnectionRoutes"): attr} 71 | 72 | def get_definition_prefix(self): 73 | return "resources.awsVPNConnectionRoutes." 74 | 75 | def create_after(self, resources, defn): 76 | return { 77 | r 78 | for r in resources 79 | if isinstance(r, aws_vpn_connection.AWSVPNConnectionState) 80 | } 81 | 82 | def realize_create_vpn_route(self, allow_recreate): 83 | config: AWSVPNConnectionRouteDefinition = self.get_defn() 84 | 85 | if self.state == self.UP: 86 | if not allow_recreate: 87 | raise Exception( 88 | "vpn connection route {} definition changed and it needs to be recreated" 89 | " use --allow-recreate if you want to create a new one".format( 90 | self.name 91 | ) 92 | ) 93 | self.warn("route definition changed, recreating ...") 94 | self._destroy() 95 | 96 | self._state["region"] = config.config.region 97 | vpn_conn_id = config.config.vpnConnectionId 98 | if vpn_conn_id.startswith("res-"): 99 | res = self.depl.get_typed_resource( 100 | vpn_conn_id[4:].split(".")[0], 101 | "aws-vpn-connection", 102 | AWSVPNConnectionState, 103 | ) 104 | vpn_conn_id = res._state["vpnConnectionId"] 105 | 106 | self.log( 107 | "creating route to {0} using vpn connection {1}".format( 108 | config.config.destinationCidrBlock, vpn_conn_id 109 | ) 110 | ) 111 | self.get_client().create_vpn_connection_route( 112 | DestinationCidrBlock=config.config.destinationCidrBlock, 113 | VpnConnectionId=vpn_conn_id, 114 | ) 115 | 116 | with self.depl._db: 117 | self.state = self.UP 118 | self._state["vpnConnectionId"] = vpn_conn_id 119 | self._state["destinationCidrBlock"] = config.config.destinationCidrBlock 120 | 121 | def _destroy(self): 122 | if self.state != self.UP: 123 | return 124 | self.log("deleting route to {}".format(self._state["destinationCidrBlock"])) 125 | try: 126 | self.get_client().delete_vpn_connection_route( 127 | DestinationCidrBlock=self._state["destinationCidrBlock"], 128 | VpnConnectionId=self._state["vpnConnectionId"], 129 | ) 130 | except botocore.exceptions.ClientError as e: 131 | if ( 132 | e.response["Error"]["Code"] == "InvalidRoute.NotFound" 133 | or e.response["Error"]["Code"] == "InvalidVpnConnectionID.NotFound" 134 | ): 135 | self.warn("route or vpn connection was already deleted") 136 | else: 137 | raise e 138 | 139 | with self.depl._db: 140 | self.state = self.MISSING 141 | self._state["vpnConnectionId"] = None 142 | self._state["destinationCidrBlock"] = None 143 | 144 | def destroy(self, wipe=True): 145 | self._destroy() 146 | return True 147 | -------------------------------------------------------------------------------- /nixops_aws/resources/ec2_common.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import socket 3 | import getpass 4 | 5 | import boto3 6 | import nixops.util 7 | import nixops.deployment 8 | import nixops.resources 9 | import nixops_aws.ec2_utils 10 | from nixops.state import StateDict 11 | from typing import Optional 12 | from boto.ec2.connection import EC2Connection 13 | from typing import Mapping, TYPE_CHECKING 14 | 15 | if TYPE_CHECKING: 16 | import mypy_boto3_ec2 17 | 18 | 19 | class EC2CommonState: 20 | depl: nixops.deployment.Deployment 21 | name: str 22 | _state: StateDict 23 | _client: Optional["mypy_boto3_ec2.EC2Client"] 24 | 25 | # Not always available 26 | _conn: Optional[EC2Connection] 27 | access_key_id: Optional[str] 28 | 29 | COMMON_EC2_RESERVED = ["accessKeyId", "ec2.tags"] 30 | 31 | def _retry(self, fun, **kwargs): 32 | return nixops_aws.ec2_utils.retry(fun, logger=self, **kwargs) 33 | 34 | tags = nixops.util.attr_property("ec2.tags", {}, "json") 35 | 36 | def get_common_tags(self) -> Mapping[str, str]: 37 | tags = { 38 | "CharonNetworkUUID": self.depl.uuid, 39 | "CharonMachineName": self.name, 40 | "CharonStateFile": "{0}@{1}:{2}".format( 41 | getpass.getuser(), socket.gethostname(), self.depl._db.db_file 42 | ), 43 | } 44 | if self.depl.name: 45 | tags["CharonNetworkName"] = self.depl.name 46 | return tags 47 | 48 | def get_default_name_tag(self): 49 | return "{0} [{1}]".format(self.depl.description, self.name) 50 | 51 | def update_tags_using(self, updater, user_tags={}, check=False): 52 | tags = {"Name": self.get_default_name_tag()} 53 | tags.update(user_tags) 54 | tags.update(self.get_common_tags()) 55 | 56 | if tags != self.tags or check: 57 | updater(tags) 58 | self.tags = tags 59 | 60 | def update_tags(self, id, user_tags={}, check=False): 61 | def updater(tags): 62 | # FIXME: handle removing tags. 63 | if self._conn is None: 64 | raise Exception("bug: self._conn is None") 65 | self._retry(lambda: self._conn.create_tags([id], tags)) 66 | 67 | self.update_tags_using(updater, user_tags=user_tags, check=check) 68 | 69 | def get_client(self): 70 | """ 71 | Generic method to get a cached EC2 AWS client or create it. 72 | """ 73 | 74 | # Here be dragons! 75 | # This class is weird and doesn't have it's full dependencies declared. 76 | # This function will _only_ work when _also_ inheriting from DiffEngineResourceState 77 | new_access_key_id = ( 78 | self.get_defn().config.accessKeyId if self.depl.definitions else None # type: ignore 79 | ) or nixops_aws.ec2_utils.get_access_key_id() 80 | if new_access_key_id is not None: 81 | self.access_key_id = new_access_key_id 82 | if self.access_key_id is None: 83 | raise Exception( 84 | "please set 'accessKeyId', $EC2_ACCESS_KEY or $AWS_ACCESS_KEY_ID" 85 | ) 86 | if hasattr(self, "_client"): 87 | if self._client: 88 | return self._client 89 | assert self._state["region"] 90 | region: str = str(self._state["region"]) 91 | (access_key_id, secret_access_key) = nixops_aws.ec2_utils.fetch_aws_secret_key( 92 | self.access_key_id 93 | ) 94 | self._client: "mypy_boto3_ec2.EC2Client" = boto3.session.Session().client( 95 | service_name="ec2", 96 | region_name=region, 97 | aws_access_key_id=access_key_id, 98 | aws_secret_access_key=secret_access_key, 99 | ) 100 | return self._client 101 | 102 | def reset_client(self): 103 | self._client = None 104 | -------------------------------------------------------------------------------- /nixops_aws/resources/ec2_keypair.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Automatic provisioning of EC2 key pairs. 4 | 5 | import nixops.util 6 | import nixops.resources 7 | import nixops_aws.ec2_utils 8 | from nixops_aws.backends import ec2 9 | 10 | from .types.ec2_keypair import Ec2KeypairOptions 11 | 12 | 13 | class EC2KeyPairDefinition(nixops.resources.ResourceDefinition): 14 | """Definition of an EC2 key pair.""" 15 | 16 | config: Ec2KeypairOptions 17 | 18 | @classmethod 19 | def get_type(cls): 20 | return "ec2-keypair" 21 | 22 | @classmethod 23 | def get_resource_type(cls): 24 | return "ec2KeyPairs" 25 | 26 | def __init__(self, name: str, config: nixops.resources.ResourceEval): 27 | nixops.resources.ResourceDefinition.__init__(self, name, config) 28 | self.keypair_name = self.config.name 29 | self.region = self.config.region 30 | self.access_key_id = self.config.accessKeyId 31 | 32 | def show_type(self): 33 | return "{0} [{1}]".format(self.get_type(), self.region) 34 | 35 | 36 | class EC2KeyPairState(nixops.resources.ResourceState[EC2KeyPairDefinition]): 37 | """State of an EC2 key pair.""" 38 | 39 | definition_type = EC2KeyPairDefinition 40 | 41 | state = nixops.util.attr_property( 42 | "state", nixops.resources.ResourceState.MISSING, int 43 | ) 44 | keypair_name = nixops.util.attr_property("ec2.keyPairName", None) 45 | public_key = nixops.util.attr_property("publicKey", None) 46 | private_key = nixops.util.attr_property("privateKey", None) 47 | access_key_id = nixops.util.attr_property("ec2.accessKeyId", None) 48 | region = nixops.util.attr_property("ec2.region", None) 49 | 50 | @classmethod 51 | def get_type(cls): 52 | return "ec2-keypair" 53 | 54 | def __init__(self, depl, name, id): 55 | nixops.resources.ResourceState.__init__(self, depl, name, id) 56 | self._conn = None 57 | 58 | def show_type(self): 59 | s = super(EC2KeyPairState, self).show_type() 60 | if self.region: 61 | s = "{0} [{1}]".format(s, self.region) 62 | return s 63 | 64 | @property 65 | def resource_id(self): 66 | return self.keypair_name 67 | 68 | def get_definition_prefix(self): 69 | return "resources.ec2KeyPairs." 70 | 71 | def _connect(self): 72 | if self._conn: 73 | return self._conn 74 | self._conn = nixops_aws.ec2_utils.connect(self.region, self.access_key_id) 75 | return self._conn 76 | 77 | def create(self, defn, check, allow_reboot, allow_recreate): 78 | 79 | self.access_key_id = ( 80 | defn.access_key_id or nixops_aws.ec2_utils.get_access_key_id() 81 | ) 82 | if not self.access_key_id: 83 | raise Exception( 84 | "please set ‘accessKeyId’, $EC2_ACCESS_KEY or $AWS_ACCESS_KEY_ID" 85 | ) 86 | 87 | # Generate the key pair locally. 88 | if not self.public_key: 89 | (private, public) = nixops.util.create_key_pair( 90 | type="rsa" 91 | ) # EC2 only supports RSA keys. 92 | with self.depl._db: 93 | self.public_key = public 94 | self.private_key = private 95 | 96 | # Upload the public key to EC2. 97 | if check or self.state != self.UP: 98 | 99 | self.region = defn.region 100 | 101 | # Sometimes EC2 DescribeKeypairs return empty list on invalid 102 | # identifiers, which results in a IndexError exception from within boto, 103 | # work around that until we figure out what is causing this. 104 | try: 105 | kp = self._connect().get_key_pair(defn.keypair_name) 106 | except IndexError: 107 | kp = None 108 | 109 | # Don't re-upload the key if it exists and we're just checking. 110 | if not kp or self.state != self.UP: 111 | if kp: 112 | self._connect().delete_key_pair(defn.keypair_name) 113 | self.log("uploading EC2 key pair ‘{0}’...".format(defn.keypair_name)) 114 | self._connect().import_key_pair( 115 | defn.keypair_name, self.public_key.encode() 116 | ) 117 | 118 | with self.depl._db: 119 | self.state = self.UP 120 | self.keypair_name = defn.keypair_name 121 | 122 | def destroy(self, wipe=False): 123 | def keypair_used(): 124 | for m in self.depl.active_resources.values(): 125 | if type(m) is ec2.EC2State: 126 | if m.key_pair == self.keypair_name: # type: ignore 127 | return m 128 | return None 129 | 130 | m = keypair_used() 131 | if m: 132 | raise Exception( 133 | "keypair ‘{0}’ is still in use by ‘{1}’ ({2})".format( 134 | self.keypair_name, m.name, m.vm_id 135 | ) 136 | ) 137 | 138 | if not self.depl.logger.confirm( 139 | "are you sure you want to destroy keypair ‘{0}’?".format(self.keypair_name) 140 | ): 141 | return False 142 | 143 | if self.state == self.UP: 144 | self.log("deleting EC2 key pair ‘{0}’...".format(self.keypair_name)) 145 | self._connect().delete_key_pair(self.keypair_name) 146 | 147 | return True 148 | 149 | def check(self): 150 | try: 151 | kp = self._connect().get_key_pair(self.keypair_name) 152 | except IndexError: 153 | kp = None 154 | if kp is None: 155 | self.state = self.MISSING 156 | -------------------------------------------------------------------------------- /nixops_aws/resources/efs_common.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import boto3 3 | from typing import Optional, TYPE_CHECKING 4 | import nixops_aws.ec2_utils 5 | 6 | if TYPE_CHECKING: 7 | import mypy_boto3_efs 8 | 9 | 10 | class EFSCommonState: 11 | access_key_id: Optional[str] 12 | region: Optional[str] 13 | _efs_client: Optional["mypy_boto3_efs.EFSClient"] = None 14 | 15 | def _get_efs_client( 16 | self, access_key_id: Optional[str] = None, region: Optional[str] = None 17 | ) -> "mypy_boto3_efs.EFSClient": 18 | if self._efs_client: 19 | return self._efs_client 20 | 21 | (access_key_id, secret_access_key) = nixops_aws.ec2_utils.fetch_aws_secret_key( 22 | access_key_id or self.access_key_id 23 | ) 24 | 25 | if region is not None: 26 | region_name = region 27 | elif self.region is not None: 28 | region_name = self.region 29 | else: 30 | raise Exception("region and self.region are None") 31 | 32 | client: "mypy_boto3_efs.EFSClient" = boto3.session.Session().client( 33 | "efs", 34 | region_name=region_name, 35 | aws_access_key_id=access_key_id, 36 | aws_secret_access_key=secret_access_key, 37 | ) 38 | self._efs_client = client 39 | 40 | return self._efs_client 41 | -------------------------------------------------------------------------------- /nixops_aws/resources/rds_db_subnet_group.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Automatic provisioning of EC2 security groups. 4 | 5 | import nixops.resources 6 | import nixops.util 7 | import nixops_aws.ec2_utils 8 | from typing import Optional, List, TYPE_CHECKING 9 | from .types.rds_db_subnet_group import RDSDbSubnetGroupOptions 10 | from .vpc_subnet import VPCSubnetState 11 | 12 | if TYPE_CHECKING: 13 | import mypy_boto3_rds 14 | 15 | 16 | class RDSDbSubnetGroupDefinition(nixops.resources.ResourceDefinition): 17 | """Definition of an RDS DB Subnet Group.""" 18 | 19 | config: RDSDbSubnetGroupOptions 20 | 21 | @classmethod 22 | def get_type(cls): 23 | return "rds-subnet-group" 24 | 25 | @classmethod 26 | def get_resource_type(cls): 27 | return "rdsSubnetGroups" 28 | 29 | 30 | class RDSDbSubnetGroupState(nixops.resources.ResourceState[RDSDbSubnetGroupDefinition]): 31 | """State of an EC2 security group.""" 32 | 33 | definition_type = RDSDbSubnetGroupDefinition 34 | group_name = nixops.util.attr_property("group_name", None) 35 | region = nixops.util.attr_property("region", None) 36 | description = nixops.util.attr_property("description", None) 37 | subnet_ids = nixops.util.attr_property("subnet_ids", [], "json") 38 | 39 | access_key_id: Optional[str] = None 40 | _rds_conn: Optional["mypy_boto3_rds.RDSClient"] = None 41 | 42 | @classmethod 43 | def get_type(cls): 44 | return "rds-subnet-group" 45 | 46 | @property 47 | def resource_id(self): 48 | return self.group_name 49 | 50 | def create_after(self, resources, defn): 51 | return {r for r in resources if isinstance(r, VPCSubnetState)} 52 | 53 | def _connect_rds(self) -> "mypy_boto3_rds.RDSClient": 54 | if self._rds_conn: 55 | return self._rds_conn 56 | self._conn = nixops_aws.ec2_utils.connect_rds_boto3( 57 | self.region, self.access_key_id 58 | ) 59 | return self._conn 60 | 61 | def create( 62 | self, 63 | defn: RDSDbSubnetGroupDefinition, 64 | check: bool, 65 | allow_reboot: bool, 66 | allow_recreate: bool, 67 | ): 68 | self.region = defn.config.region 69 | self.access_key_id = ( 70 | defn.config.accessKeyId or nixops_aws.ec2_utils.get_access_key_id() 71 | ) 72 | 73 | if not self.access_key_id: 74 | raise Exception( 75 | "please set ‘accessKeyId’, $EC2_ACCESS_KEY or $AWS_ACCESS_KEY_ID" 76 | ) 77 | 78 | if self.state == self.UP and (self.region != defn.config.region): 79 | raise Exception( 80 | "changing the region of an RDS Subnet Group is not supported" 81 | ) 82 | if self.state == self.UP and (self.group_name != defn.config.name): 83 | raise Exception("changing the name of an RDS Subnet Group is not supported") 84 | 85 | self.group_name = defn.config.name 86 | self.description = defn.config.description 87 | 88 | subnets: List[str] = [] 89 | for s in defn.config.subnetIds: 90 | if s.startswith("res-"): 91 | res = self.depl.get_typed_resource( 92 | s[4:].split(".")[0], "vpc-subnet", VPCSubnetState 93 | ) 94 | subnets.append(res._state["subnetId"]) 95 | else: 96 | subnets.append(s) 97 | 98 | if self.state != self.UP: 99 | self.logger.log(f"Creating RDS Subnet Group {self.group_name}") 100 | self._connect_rds().create_db_subnet_group( 101 | DBSubnetGroupName=self.group_name, 102 | DBSubnetGroupDescription=self.description, 103 | SubnetIds=subnets, 104 | ) 105 | self.state = self.UP 106 | else: 107 | self._connect_rds().modify_db_subnet_group( 108 | DBSubnetGroupName=self.group_name, 109 | DBSubnetGroupDescription=self.description, 110 | SubnetIds=subnets, 111 | ) 112 | 113 | def destroy(self, wipe=False): 114 | 115 | self.access_key_id = nixops_aws.ec2_utils.get_access_key_id() 116 | client = self._connect_rds() 117 | try: 118 | nixops_aws.ec2_utils.retry( 119 | lambda: client.delete_db_subnet_group( 120 | DBSubnetGroupName=self.group_name 121 | ), 122 | error_codes=["DependencyViolation"], 123 | logger=self.logger, 124 | ) 125 | except client.exceptions.DBSubnetGroupNotFoundFault: 126 | pass 127 | self.state = self.MISSING 128 | return True 129 | -------------------------------------------------------------------------------- /nixops_aws/resources/sqs_queue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Automatic provisioning of AWS SQS queues. 4 | 5 | import time 6 | import boto.sqs 7 | import nixops.util 8 | import nixops.resources 9 | import nixops_aws.ec2_utils 10 | from .types.sqs_queue import SqsQueueOptions 11 | 12 | 13 | class SQSQueueDefinition(nixops.resources.ResourceDefinition): 14 | """Definition of an SQS queue.""" 15 | 16 | config: SqsQueueOptions 17 | 18 | @classmethod 19 | def get_type(cls): 20 | return "sqs-queue" 21 | 22 | @classmethod 23 | def get_resource_type(cls): 24 | return "sqsQueues" 25 | 26 | def __init__(self, name: str, config: nixops.resources.ResourceEval): 27 | super().__init__(name, config) 28 | self.queue_name = self.config.name 29 | self.region = self.config.region 30 | self.access_key_id = self.config.accessKeyId 31 | self.visibility_timeout = self.config.visibilityTimeout 32 | 33 | def show_type(self): 34 | return "{0} [{1}]".format(self.get_type(), self.region) 35 | 36 | 37 | class SQSQueueState(nixops.resources.ResourceState[SQSQueueDefinition]): 38 | """State of an SQS queue.""" 39 | 40 | definition_type = SQSQueueDefinition 41 | 42 | state = nixops.util.attr_property( 43 | "state", nixops.resources.ResourceState.MISSING, int 44 | ) 45 | queue_name = nixops.util.attr_property("ec2.queueName", None) 46 | access_key_id = nixops.util.attr_property("ec2.accessKeyId", None) 47 | region = nixops.util.attr_property("ec2.region", None) 48 | visibility_timeout = nixops.util.attr_property("ec2.queueVisibilityTimeout", None) 49 | url = nixops.util.attr_property("ec2.queueURL", None) 50 | arn = nixops.util.attr_property("ec2.queueARN", None) 51 | 52 | @classmethod 53 | def get_type(cls): 54 | return "sqs-queue" 55 | 56 | def __init__(self, depl, name, id): 57 | nixops.resources.ResourceState.__init__(self, depl, name, id) 58 | self._conn = None 59 | 60 | def show_type(self): 61 | s = super(SQSQueueState, self).show_type() 62 | if self.region: 63 | s = "{0} [{1}]".format(s, self.region) 64 | return s 65 | 66 | def prefix_definition(self, attr): 67 | return {("resources", "sqsQueues"): attr} 68 | 69 | def get_physical_spec(self): 70 | return {"url": self.url, "arn": self.arn} 71 | 72 | @property 73 | def resource_id(self): 74 | return self.queue_name 75 | 76 | def _connect(self): 77 | if self._conn: 78 | return self._conn 79 | assert self.region 80 | (access_key_id, secret_access_key) = nixops_aws.ec2_utils.fetch_aws_secret_key( 81 | self.access_key_id 82 | ) 83 | self._conn = boto.sqs.connect_to_region( 84 | region_name=self.region, 85 | aws_access_key_id=access_key_id, 86 | aws_secret_access_key=secret_access_key, 87 | ) 88 | return self._conn 89 | 90 | def _destroy(self): 91 | if self.state != self.UP: 92 | return 93 | conn = self._connect() 94 | q = conn.lookup(self.queue_name) 95 | if q: 96 | self.log("destroying SQS queue ‘{0}’...".format(self.queue_name)) 97 | conn.delete_queue(q) 98 | with self.depl._db: 99 | self.state = self.MISSING 100 | self.queue_name = None 101 | self.queue_base_name = None 102 | self.url = None 103 | self.arn = None 104 | self.region = None 105 | self.access_key_id = None 106 | 107 | def create(self, defn, check, allow_reboot, allow_recreate): 108 | 109 | self.access_key_id = ( 110 | defn.access_key_id or nixops_aws.ec2_utils.get_access_key_id() 111 | ) 112 | if not self.access_key_id: 113 | raise Exception( 114 | "please set ‘accessKeyId’, $EC2_ACCESS_KEY or $AWS_ACCESS_KEY_ID" 115 | ) 116 | 117 | if self.state == self.UP and ( 118 | self.queue_name != defn.queue_name or self.region != defn.region 119 | ): 120 | self.log("queue definition changed, recreating...") 121 | self._destroy() 122 | self._conn = None # necessary if region changed 123 | 124 | if check or self.state != self.UP: 125 | 126 | self.region = defn.region 127 | 128 | q = self._connect().lookup(defn.queue_name) 129 | 130 | if not q or self.state != self.UP: 131 | if q: 132 | # SQS requires us to wait for 60 seconds to 133 | # recreate a queue. 134 | self.log( 135 | "deleting queue ‘{0}’ (and waiting 60 seconds)...".format( 136 | defn.queue_name 137 | ) 138 | ) 139 | self._connect().delete_queue(q) 140 | time.sleep(61) 141 | self.log("creating SQS queue ‘{0}’...".format(defn.queue_name)) 142 | q = nixops_aws.ec2_utils.retry( 143 | lambda: self._connect().create_queue( 144 | defn.queue_name, defn.visibility_timeout 145 | ), 146 | error_codes=["AWS.SimpleQueueService.QueueDeletedRecently"], 147 | ) 148 | 149 | with self.depl._db: 150 | self.state = self.UP 151 | self.queue_name = defn.queue_name 152 | self.url = q.url 153 | self.arn = q.get_attributes()["QueueArn"] 154 | 155 | def destroy(self, wipe=False): 156 | self._destroy() 157 | return True 158 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NixOS/nixops-aws/d173b2f14ec767d782ceab45fb22b32fe3b5a1f7/nixops_aws/resources/types/__init__.py -------------------------------------------------------------------------------- /nixops_aws/resources/types/aws_data_lifecycle_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping 2 | from typing import Union 3 | from typing import Optional 4 | from typing_extensions import Literal 5 | from nixops.resources import ResourceOptions 6 | 7 | 8 | class AwsDataLifecycleManagerOptions(ResourceOptions): 9 | accessKeyId: Optional[str] 10 | region: str 11 | dlmName: str 12 | policyId: Optional[str] 13 | description: Optional[str] 14 | executionRole: str 15 | resourceTypes: Union[ 16 | Literal["instance"], Literal["volume"], 17 | ] 18 | targetTags: Mapping[str, str] 19 | excludeBootVolume: bool 20 | copyTags: bool 21 | tagsToAdd: Mapping[str, str] 22 | ruleInterval: Union[ 23 | Literal[2], 24 | Literal[3], 25 | Literal[4], 26 | Literal[6], 27 | Literal[8], 28 | Literal[12], 29 | Literal[24], 30 | ] 31 | ruleIntervalUnit: Union[ 32 | Literal["hours"], 33 | ] 34 | ruleTime: str 35 | retainRule: int 36 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/aws_vpn_connection.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Mapping 3 | 4 | 5 | class AwsVpnConnectionOptions(ResourceOptions): 6 | accessKeyId: str 7 | customerGatewayId: str 8 | name: str 9 | region: str 10 | staticRoutesOnly: bool 11 | tags: Mapping[str, str] 12 | vpnGatewayId: str 13 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/aws_vpn_connection_route.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Optional 3 | 4 | 5 | class AwsVpnConnectionRouteOptions(ResourceOptions): 6 | accessKeyId: str 7 | destinationCidrBlock: Optional[str] 8 | name: str 9 | region: str 10 | vpnConnectionId: str 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/aws_vpn_gateway.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Mapping 3 | 4 | 5 | class AwsVpnGatewayOptions(ResourceOptions): 6 | accessKeyId: str 7 | name: str 8 | region: str 9 | tags: Mapping[str, str] 10 | vpcId: str 11 | zone: str 12 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/cloudwatch_log_group.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Optional 3 | 4 | 5 | class CloudwatchLogGroupOptions(ResourceOptions): 6 | accessKeyId: str 7 | arn: str 8 | name: str 9 | region: str 10 | retentionInDays: Optional[int] 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/cloudwatch_log_stream.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class CloudwatchLogStreamOptions(ResourceOptions): 5 | accessKeyId: str 6 | arn: str 7 | logGroupName: str 8 | name: str 9 | region: str 10 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/cloudwatch_metric_alarm.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from nixops.resources import ResourceOptions 3 | from typing_extensions import Literal 4 | from typing import Sequence 5 | 6 | 7 | class DimensionsOptions(ResourceOptions): 8 | Name: str 9 | Value: str 10 | 11 | 12 | class CloudwatchMetricAlarmOptions(ResourceOptions): 13 | accessKeyId: str 14 | alarmActions: Sequence[str] 15 | comparisonOperator: Union[ 16 | Literal["GreaterThanOrEqualToThreshold"], 17 | Literal["GreaterThanThreshold"], 18 | Literal["LessThanThreshold"], 19 | Literal["LessThanOrEqualToThreshold"], 20 | ] 21 | datapointsToAlarm: int 22 | dimensions: Sequence[DimensionsOptions] 23 | evaluationPeriods: int 24 | insufficientDataActions: Sequence[str] 25 | metricName: str 26 | name: str 27 | namespace: str 28 | okActions: Sequence[str] 29 | period: int 30 | region: str 31 | statistic: Union[ 32 | Literal["SampleCount"], 33 | Literal["Average"], 34 | Literal["Sum"], 35 | Literal["Minimum"], 36 | Literal["Maximum"], 37 | ] 38 | threshold: int 39 | treatMissingData: Union[ 40 | Literal["breaching"], 41 | Literal["notBreaching"], 42 | Literal["ignore"], 43 | Literal["missing"], 44 | ] 45 | unit: Union[ 46 | Literal["Seconds"], 47 | Literal["Microseconds"], 48 | Literal["Milliseconds"], 49 | Literal["Bytes"], 50 | Literal["Kilobytes"], 51 | Literal["Megabytes"], 52 | Literal["Gigabytes"], 53 | Literal["Terabytes"], 54 | Literal["Bits"], 55 | Literal["Kilobits"], 56 | Literal["Megabits"], 57 | Literal["Gigabits"], 58 | Literal["Terabits"], 59 | Literal["Percent"], 60 | Literal["Count"], 61 | Literal["Bytes/Second"], 62 | Literal["Kilobytes/Second"], 63 | Literal["Megabytes/Second"], 64 | Literal["Gigabytes/Second"], 65 | Literal["Terabytes/Second"], 66 | Literal["Bits/Second"], 67 | Literal["Kilobits/Second"], 68 | Literal["Megabits/Second"], 69 | Literal["Gigabits/Second"], 70 | Literal["Terabits/Second"], 71 | Literal["Count/Second"], 72 | Literal["None"], 73 | ] 74 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/ebs_volume.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from nixops.resources import ResourceOptions 3 | from typing import Mapping 4 | from typing import Optional 5 | from typing_extensions import Literal 6 | 7 | 8 | class EbsVolumeOptions(ResourceOptions): 9 | accessKeyId: Optional[str] 10 | iops: Optional[int] 11 | region: str 12 | size: int 13 | snapshot: str 14 | tags: Mapping[str, str] 15 | volumeId: Optional[str] 16 | volumeType: Union[ 17 | Literal["standard"], 18 | Literal["io1"], 19 | Literal["io2"], 20 | Literal["gp2"], 21 | Literal["gp3"], 22 | Literal["st1"], 23 | Literal["sc1"], 24 | ] 25 | zone: str 26 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/ec2_keypair.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class Ec2KeypairOptions(ResourceOptions): 5 | accessKeyId: str 6 | name: str 7 | region: str 8 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/ec2_placement_group.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class Ec2PlacementGroupOptions(ResourceOptions): 5 | accessKeyId: str 6 | name: str 7 | region: str 8 | strategy: str 9 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/ec2_rds_dbinstance.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Sequence, Optional 3 | 4 | 5 | class Ec2RdsDbinstanceOptions(ResourceOptions): 6 | accessKeyId: str 7 | allocatedStorage: int 8 | dbName: str 9 | endpoint: str 10 | engine: str 11 | id: str 12 | instanceClass: str 13 | masterPassword: str 14 | masterUsername: str 15 | multiAZ: bool 16 | port: int 17 | region: str 18 | subnetGroup: Optional[str] 19 | securityGroups: Sequence[str] 20 | vpcSecurityGroups: Optional[Sequence[str]] 21 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/ec2_rds_dbsecurity_group.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence 2 | from nixops.resources import ResourceOptions 3 | from typing import Optional 4 | 5 | 6 | class RulesOptions(ResourceOptions): 7 | cidrIp: Optional[str] 8 | securityGroupId: Optional[str] 9 | securityGroupName: Optional[str] 10 | securityGroupOwnerId: Optional[str] 11 | 12 | 13 | class Ec2RdsDbsecurityGroupOptions(ResourceOptions): 14 | accessKeyId: str 15 | description: str 16 | groupName: str 17 | region: str 18 | rules: Sequence[RulesOptions] 19 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/ec2_security_group.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Optional 3 | from typing import Sequence 4 | 5 | 6 | class SourcegroupOptions(ResourceOptions): 7 | groupName: Optional[str] 8 | ownerId: Optional[str] 9 | 10 | 11 | class RulesOptions(ResourceOptions): 12 | codeNumber: Optional[int] 13 | fromPort: Optional[int] 14 | protocol: str 15 | sourceGroup: SourcegroupOptions 16 | sourceIp: Optional[str] 17 | toPort: Optional[int] 18 | typeNumber: Optional[int] 19 | 20 | 21 | class Ec2SecurityGroupOptions(ResourceOptions): 22 | accessKeyId: str 23 | description: str 24 | groupId: Optional[str] 25 | name: str 26 | region: str 27 | rules: Sequence[RulesOptions] 28 | vpcId: Optional[str] 29 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/elastic_file_system.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping, Optional 2 | from nixops.resources import ResourceOptions 3 | 4 | 5 | class ElasticFileSystemOptions(ResourceOptions): 6 | accessKeyId: Optional[str] 7 | region: str 8 | tags: Mapping[str, str] 9 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/elastic_file_system_mount_target.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping 2 | from nixops.resources import ResourceOptions 3 | from typing import Sequence 4 | from typing import Optional 5 | 6 | 7 | class ElasticFileSystemMountTargetOptions(ResourceOptions): 8 | accessKeyId: str 9 | fileSystem: str 10 | ipAddress: Optional[str] 11 | region: str 12 | securityGroups: Sequence[str] 13 | subnet: str 14 | tags: Mapping[str, str] 15 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/elastic_ip.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class ElasticIpOptions(ResourceOptions): 5 | accessKeyId: str 6 | address: str 7 | region: str 8 | vpc: bool 9 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/iam_role.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping 2 | from nixops.resources import ResourceOptions 3 | 4 | 5 | class IamRoleOptions(ResourceOptions): 6 | accessKeyId: str 7 | assumeRolePolicy: str 8 | name: str 9 | policy: str 10 | tags: Mapping[str, str] 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/rds_db_subnet_group.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence 2 | from nixops.resources import ResourceOptions 3 | 4 | 5 | class RDSDbSubnetGroupOptions(ResourceOptions): 6 | accessKeyId: str 7 | description: str 8 | name: str 9 | region: str 10 | subnetIds: Sequence[str] 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/route53_health_check.py: -------------------------------------------------------------------------------- 1 | from typing_extensions import Literal 2 | from typing import Optional 3 | from nixops.resources import ResourceOptions 4 | from typing import Union 5 | from typing import Sequence 6 | 7 | 8 | class AlarmidentifierOptions(ResourceOptions): 9 | name: str 10 | region: str 11 | 12 | 13 | class Route53HealthCheckOptions(ResourceOptions): 14 | accessKeyId: str 15 | alarmIdentifier: AlarmidentifierOptions 16 | childHealthChecks: Sequence[str] 17 | enableSNI: bool 18 | failureThreshold: int 19 | fullyQualifiedDomainName: str 20 | healthThreshold: int 21 | insufficientDataHealthStatus: Optional[ 22 | Union[Literal["Healthy"], Literal["Unhealthy"], Literal["LastKnownStatus"]] 23 | ] 24 | inverted: bool 25 | ipAddress: Optional[str] 26 | measureLatency: bool 27 | name: str 28 | port: Optional[int] 29 | regions: Sequence[str] 30 | requestInterval: Union[Literal["10"], Literal["30"]] 31 | resourcePath: str 32 | searchString: str 33 | type: Union[ 34 | Literal["HTTP"], 35 | Literal["HTTPS"], 36 | Literal["HTTP_STR_MATCH"], 37 | Literal["HTTPS_STR_MATCH"], 38 | Literal["TCP"], 39 | Literal["CALCULATED"], 40 | Literal["CLOUDWATCH_METRIC"], 41 | ] 42 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/route53_hosted_zone.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Sequence 3 | 4 | 5 | class AssociatedvpcsOptions(ResourceOptions): 6 | region: str 7 | vpcId: str 8 | 9 | 10 | class Route53HostedZoneOptions(ResourceOptions): 11 | accessKeyId: str 12 | associatedVPCs: Sequence[AssociatedvpcsOptions] 13 | comment: str 14 | name: str 15 | privateZone: bool 16 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/route53_recordset.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Union 3 | from typing import Sequence 4 | from typing import Optional 5 | from typing_extensions import Literal 6 | 7 | 8 | class Route53RecordsetOptions(ResourceOptions): 9 | accessKeyId: str 10 | domainName: str 11 | healthCheckId: str 12 | name: str 13 | recordType: Union[ 14 | Literal["A"], 15 | Literal["AAAA"], 16 | Literal["TXT"], 17 | Literal["CNAME"], 18 | Literal["MX"], 19 | Literal["NAPT"], 20 | Literal["PTR"], 21 | Literal["SRV"], 22 | Literal["SPF"], 23 | ] 24 | recordValues: Sequence[str] 25 | routingPolicy: Union[Literal["simple"], Literal["weighted"], Literal["multivalue"]] 26 | setIdentifier: str 27 | ttl: int 28 | weight: int 29 | zoneId: Optional[str] 30 | zoneName: Optional[str] 31 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/s3_bucket.py: -------------------------------------------------------------------------------- 1 | from typing_extensions import Literal 2 | from nixops.resources import ResourceOptions 3 | from typing import Union 4 | 5 | 6 | class WebsiteOptions(ResourceOptions): 7 | enabled: bool 8 | errorDocument: str 9 | suffix: str 10 | 11 | 12 | class S3BucketOptions(ResourceOptions): 13 | accessKeyId: str 14 | arn: str 15 | lifeCycle: str 16 | name: str 17 | persistOnDestroy: bool 18 | policy: str 19 | region: str 20 | versioning: Union[Literal["Suspended"], Literal["Enabled"]] 21 | website: WebsiteOptions 22 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/sns_topic.py: -------------------------------------------------------------------------------- 1 | from typing import Sequence 2 | from nixops.resources import ResourceOptions 3 | from typing import Optional 4 | 5 | 6 | class SubscriptionsOptions(ResourceOptions): 7 | endpoint: str 8 | protocol: str 9 | 10 | 11 | class SnsTopicOptions(ResourceOptions): 12 | accessKeyId: Optional[str] 13 | arn: str 14 | displayName: Optional[str] 15 | name: str 16 | policy: str 17 | region: str 18 | subscriptions: Sequence[SubscriptionsOptions] 19 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/sqs_queue.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class SqsQueueOptions(ResourceOptions): 5 | accessKeyId: str 6 | arn: str 7 | name: str 8 | region: str 9 | url: str 10 | visibilityTimeout: int 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping 2 | from nixops.resources import ResourceOptions 3 | 4 | 5 | class VpcOptions(ResourceOptions): 6 | accessKeyId: str 7 | amazonProvidedIpv6CidrBlock: bool 8 | cidrBlock: str 9 | enableClassicLink: bool 10 | enableDnsHostnames: bool 11 | enableDnsSupport: bool 12 | instanceTenancy: str 13 | name: str 14 | region: str 15 | tags: Mapping[str, str] 16 | vpcId: str 17 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_customer_gateway.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Mapping 3 | 4 | 5 | class VpcCustomerGatewayOptions(ResourceOptions): 6 | accessKeyId: str 7 | bgpAsn: int 8 | name: str 9 | publicIp: str 10 | region: str 11 | tags: Mapping[str, str] 12 | type: str 13 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_dhcp_options.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping 2 | from nixops.resources import ResourceOptions 3 | from typing import Sequence 4 | from typing import Optional 5 | 6 | 7 | class VpcDhcpOptionsOptions(ResourceOptions): 8 | accessKeyId: str 9 | domainName: Optional[str] 10 | domainNameServers: Optional[Sequence[str]] 11 | name: str 12 | netbiosNameServers: Optional[Sequence[str]] 13 | netbiosNodeType: Optional[int] 14 | ntpServers: Optional[Sequence[str]] 15 | region: str 16 | tags: Mapping[str, str] 17 | vpcId: str 18 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_egress_only_internet_gateway.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class VpcEgressOnlyInternetGatewayOptions(ResourceOptions): 5 | accessKeyId: str 6 | name: str 7 | region: str 8 | vpcId: str 9 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_endpoint.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Optional 3 | from typing import Sequence 4 | 5 | 6 | class VpcEndpointOptions(ResourceOptions): 7 | accessKeyId: str 8 | name: str 9 | policy: Optional[str] 10 | region: str 11 | routeTableIds: Sequence[str] 12 | serviceName: str 13 | vpcId: str 14 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_internet_gateway.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Mapping 3 | 4 | 5 | class VpcInternetGatewayOptions(ResourceOptions): 6 | accessKeyId: str 7 | name: str 8 | region: str 9 | tags: Mapping[str, str] 10 | vpcId: str 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_nat_gateway.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class VpcNatGatewayOptions(ResourceOptions): 5 | accessKeyId: str 6 | allocationId: str 7 | name: str 8 | region: str 9 | subnetId: str 10 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_network_acl.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Sequence 3 | from typing import Mapping 4 | from typing import Optional 5 | 6 | 7 | class EntriesOptions(ResourceOptions): 8 | cidrBlock: Optional[str] 9 | egress: bool 10 | fromPort: Optional[int] 11 | icmpCode: Optional[int] 12 | icmpType: Optional[int] 13 | ipv6CidrBlock: Optional[str] 14 | protocol: str 15 | ruleAction: str 16 | ruleNumber: int 17 | toPort: Optional[int] 18 | 19 | 20 | class VpcNetworkAclOptions(ResourceOptions): 21 | accessKeyId: str 22 | entries: Sequence[EntriesOptions] 23 | name: str 24 | networkAclId: str 25 | region: str 26 | subnetIds: Sequence[str] 27 | tags: Mapping[str, str] 28 | vpcId: str 29 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_network_interface.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from typing import Sequence 3 | from typing import Mapping 4 | from nixops.resources import ResourceOptions 5 | 6 | 7 | class VpcNetworkInterfaceOptions(ResourceOptions): 8 | accessKeyId: str 9 | description: str 10 | name: str 11 | primaryPrivateIpAddress: Optional[str] 12 | privateIpAddresses: Sequence[str] 13 | region: str 14 | secondaryPrivateIpAddressCount: Optional[int] 15 | securityGroups: Sequence[str] 16 | sourceDestCheck: bool 17 | subnetId: str 18 | tags: Mapping[str, str] 19 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_network_interface_attachment.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class VpcNetworkInterfaceAttachmentOptions(ResourceOptions): 5 | accessKeyId: str 6 | deviceIndex: int 7 | instanceId: str 8 | name: str 9 | networkInterfaceId: str 10 | region: str 11 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_route.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Optional 3 | 4 | 5 | class VpcRouteOptions(ResourceOptions): 6 | accessKeyId: str 7 | destinationCidrBlock: Optional[str] 8 | destinationIpv6CidrBlock: Optional[str] 9 | egressOnlyInternetGatewayId: Optional[str] 10 | gatewayId: Optional[str] 11 | instanceId: Optional[str] 12 | name: str 13 | natGatewayId: Optional[str] 14 | networkInterfaceId: Optional[str] 15 | region: str 16 | routeTableId: str 17 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_route_table.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | from typing import Mapping 3 | from typing import Sequence 4 | 5 | 6 | class VpcRouteTableOptions(ResourceOptions): 7 | accessKeyId: str 8 | name: str 9 | propagatingVgws: Sequence[str] 10 | region: str 11 | tags: Mapping[str, str] 12 | vpcId: str 13 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_route_table_association.py: -------------------------------------------------------------------------------- 1 | from nixops.resources import ResourceOptions 2 | 3 | 4 | class VpcRouteTableAssociationOptions(ResourceOptions): 5 | accessKeyId: str 6 | name: str 7 | region: str 8 | routeTableId: str 9 | subnetId: str 10 | -------------------------------------------------------------------------------- /nixops_aws/resources/types/vpc_subnet.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from typing import Mapping 3 | from nixops.resources import ResourceOptions 4 | 5 | 6 | class VpcSubnetOptions(ResourceOptions): 7 | accessKeyId: str 8 | cidrBlock: str 9 | ipv6CidrBlock: Optional[str] 10 | mapPublicIpOnLaunch: bool 11 | name: str 12 | region: str 13 | subnetId: str 14 | tags: Mapping[str, str] 15 | vpcId: str 16 | zone: str 17 | -------------------------------------------------------------------------------- /nixops_aws/resources/vpc_customer_gateway.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Automatic provisioning of AWS VPC customer gateways. 4 | 5 | 6 | import botocore 7 | 8 | from nixops.state import StateDict 9 | from nixops.diff import Handler 10 | import nixops.util 11 | import nixops.resources 12 | from nixops_aws.resources.ec2_common import EC2CommonState 13 | 14 | from .types.vpc_customer_gateway import VpcCustomerGatewayOptions 15 | 16 | 17 | class VPCCustomerGatewayDefinition(nixops.resources.ResourceDefinition): 18 | """Definition of a VPC customer gateway.""" 19 | 20 | config: VpcCustomerGatewayOptions 21 | 22 | @classmethod 23 | def get_type(cls): 24 | return "vpc-customer-gateway" 25 | 26 | @classmethod 27 | def get_resource_type(cls): 28 | return "vpcCustomerGateways" 29 | 30 | def show_type(self): 31 | return "{0}".format(self.get_type()) 32 | 33 | 34 | class VPCCustomerGatewayState(nixops.resources.DiffEngineResourceState, EC2CommonState): 35 | """State of a VPC customer gateway.""" 36 | 37 | definition_type = VPCCustomerGatewayDefinition 38 | 39 | state = nixops.util.attr_property( 40 | "state", nixops.resources.DiffEngineResourceState.MISSING, int 41 | ) 42 | access_key_id = nixops.util.attr_property("accessKeyId", None) 43 | _reserved_keys = EC2CommonState.COMMON_EC2_RESERVED + ["customerGatewayId"] 44 | 45 | def __init__(self, depl, name, id): 46 | nixops.resources.DiffEngineResourceState.__init__(self, depl, name, id) 47 | self._state = StateDict(depl, id) 48 | self.handle_create_customer_gtw = Handler( 49 | ["region", "publicIp", "bgpAsn", "type"], 50 | handle=self.realize_create_customer_gtw, 51 | ) 52 | self.handle_tag_update = Handler( 53 | ["tags"], 54 | after=[self.handle_create_customer_gtw], 55 | handle=self.realize_update_tag, 56 | ) 57 | 58 | @classmethod 59 | def get_type(cls): 60 | return "vpc-customer-gateway" 61 | 62 | def show_type(self): 63 | s = super(VPCCustomerGatewayState, self).show_type() 64 | if self._state.get("region", None): 65 | s = "{0} [{1}]".format(s, self._state["region"]) 66 | return s 67 | 68 | @property 69 | def resource_id(self): 70 | return self._state.get("customerGatewayId", None) 71 | 72 | def prefix_definition(self, attr): 73 | return {("resources", "vpcCustomerGateways"): attr} 74 | 75 | def get_defintion_prefix(self): 76 | return "resources.vpcCustomerGateways." 77 | 78 | def realize_create_customer_gtw(self, allow_recreate): 79 | config: VPCCustomerGatewayDefinition = self.get_defn() 80 | if self.state == self.UP: 81 | if not allow_recreate: 82 | raise Exception( 83 | "customer gateway {} defintion changed" 84 | " use --allow-recreate if you want to create a new one".format( 85 | self._state["customerGatewayId"] 86 | ) 87 | ) 88 | self.warn("customer gateway changed, recreating...") 89 | self._destroy() 90 | 91 | self._state["region"] = config.config.region 92 | 93 | self.log("creating customer gateway") 94 | response = self.get_client().create_customer_gateway( 95 | BgpAsn=config.config.bgpAsn, 96 | PublicIp=config.config.publicIp, 97 | Type=config.config.type, 98 | ) 99 | 100 | customer_gtw_id = response["CustomerGateway"]["CustomerGatewayId"] 101 | with self.depl._db: 102 | self.state = self.STARTING 103 | 104 | waiter = self.get_client().get_waiter("customer_gateway_available") 105 | waiter.wait(CustomerGatewayIds=[customer_gtw_id]) 106 | 107 | with self.depl._db: 108 | self.state = self.UP 109 | self._state["region"] = config.config.region 110 | self._state["customerGatewayId"] = customer_gtw_id 111 | self._state["bgpAsn"] = config.config.bgpAsn 112 | self._state["publicIp"] = config.config.publicIp 113 | self._state["type"] = config.config.type 114 | 115 | def realize_update_tag(self, allow_recreate): 116 | config: VPCCustomerGatewayDefinition = self.get_defn() 117 | tags = {k: v for k, v in config.config.tags.items()} 118 | tags.update(self.get_common_tags()) 119 | self.get_client().create_tags( 120 | Resources=[self._state["customerGatewayId"]], 121 | Tags=[{"Key": k, "Value": tags[k]} for k in tags], 122 | ) 123 | 124 | def _destroy(self): 125 | if self.state != self.UP: 126 | return 127 | self.log( 128 | "deleting customer gateway {}".format(self._state["customerGatewayId"]) 129 | ) 130 | try: 131 | self.get_client().delete_customer_gateway( 132 | CustomerGatewayId=self._state["customerGatewayId"] 133 | ) 134 | except botocore.exceptions.ClientError as e: 135 | if e.response["Error"]["Code"] == "InvalidCustomerGatewayID.NotFound": 136 | self.warn( 137 | "customer gateway {} was already deleted".format( 138 | self._state["customerGatewayId"] 139 | ) 140 | ) 141 | else: 142 | raise e 143 | 144 | # TODO wait for customer gtw to be deleted 145 | with self.depl._db: 146 | self.state = self.MISSING 147 | self._state["region"] = None 148 | self._state["customerGatewayId"] = None 149 | self._state["bgpAsn"] = None 150 | self._state["publicIp"] = None 151 | self._state["type"] = None 152 | 153 | def destroy(self, wipe=False): 154 | self._destroy() 155 | return True 156 | -------------------------------------------------------------------------------- /nixops_aws/resources/vpc_egress_only_internet_gateway.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from nixops.state import StateDict 5 | from nixops.diff import Handler 6 | import nixops.util 7 | import nixops.resources 8 | from nixops_aws.resources.ec2_common import EC2CommonState 9 | from . import vpc, elastic_ip 10 | from .vpc import VPCState 11 | 12 | from .types.vpc_egress_only_internet_gateway import VpcEgressOnlyInternetGatewayOptions 13 | 14 | 15 | class VPCEgressOnlyInternetGatewayDefinition(nixops.resources.ResourceDefinition): 16 | """Definition of a VPC egress only internet gateway.""" 17 | 18 | config: VpcEgressOnlyInternetGatewayOptions 19 | 20 | @classmethod 21 | def get_type(cls): 22 | return "vpc-egress-only-internet-gateway" 23 | 24 | @classmethod 25 | def get_resource_type(cls): 26 | return "vpcEgressOnlyInternetGateways" 27 | 28 | def show_type(self): 29 | return "{0}".format(self.get_type()) 30 | 31 | 32 | class VPCEgressOnlyInternetGatewayState( 33 | nixops.resources.DiffEngineResourceState, EC2CommonState 34 | ): 35 | """State of a VPC egress only internet gateway.""" 36 | 37 | definition_type = VPCEgressOnlyInternetGatewayDefinition 38 | 39 | state = nixops.util.attr_property( 40 | "state", nixops.resources.DiffEngineResourceState.MISSING, int 41 | ) 42 | access_key_id = nixops.util.attr_property("accessKeyId", None) 43 | _reserved_keys = EC2CommonState.COMMON_EC2_RESERVED + [ 44 | "egressOnlyInternetGatewayId" 45 | ] 46 | 47 | def __init__(self, depl, name, id): 48 | nixops.resources.DiffEngineResourceState.__init__(self, depl, name, id) 49 | self._state = StateDict(depl, id) 50 | self.handle_create_igw = Handler( 51 | ["region", "vpcId"], handle=self.realize_create_gtw 52 | ) 53 | 54 | @classmethod 55 | def get_type(cls): 56 | return "vpc-egress-only-internet-gateway" 57 | 58 | def show_type(self): 59 | s = super(VPCEgressOnlyInternetGatewayState, self).show_type() 60 | if self._state.get("region", None): 61 | s = "{0} [{1}]".format(s, self._state["region"]) 62 | return s 63 | 64 | @property 65 | def resource_id(self): 66 | return self._state.get("egressOnlyInternetGatewayId", None) 67 | 68 | def prefix_definition(self, attr): 69 | return {("resources", "vpcEgressOnlyInternetGateways"): attr} 70 | 71 | def get_defintion_prefix(self): 72 | return "resources.vpcEgressOnlyInternetGateways." 73 | 74 | def create_after(self, resources, defn): 75 | return { 76 | r 77 | for r in resources 78 | if isinstance(r, vpc.VPCState) or isinstance(r, elastic_ip.ElasticIPState) 79 | } 80 | 81 | def realize_create_gtw(self, allow_recreate): 82 | config: VPCEgressOnlyInternetGatewayDefinition = self.get_defn() 83 | 84 | if self.state == self.UP: 85 | if not allow_recreate: 86 | raise Exception( 87 | "egress only internet gateway {} defintion changed" 88 | " use --allow-recreate if you want to create a new one".format( 89 | self._state["egressOnlyInternetGatewayId"] 90 | ) 91 | ) 92 | self.warn("egress only internet gateway changed, recreating...") 93 | self._destroy() 94 | 95 | self._state["region"] = config.config.region 96 | 97 | vpc_id = config.config.vpcId 98 | if vpc_id.startswith("res-"): 99 | res = self.depl.get_typed_resource( 100 | vpc_id[4:].split(".")[0], "vpc", VPCState 101 | ) 102 | vpc_id = res._state["vpcId"] 103 | 104 | self.log( 105 | "creating egress only internet gateway in region {0}, vpc {1}".format( 106 | self._state["region"], vpc_id 107 | ) 108 | ) 109 | response = self.get_client().create_egress_only_internet_gateway(VpcId=vpc_id) 110 | igw_id = response["EgressOnlyInternetGateway"]["EgressOnlyInternetGatewayId"] 111 | 112 | with self.depl._db: 113 | self.state = self.UP 114 | self._state["region"] = config.config.region 115 | self._state["vpcId"] = vpc_id 116 | self._state["egressOnlyInternetGatewayId"] = igw_id 117 | 118 | def _destroy(self): 119 | if self.state != self.UP: 120 | return 121 | self.log( 122 | "deleting egress only internet gateway {0}".format( 123 | self._state["egressOnlyInternetGatewayId"] 124 | ) 125 | ) 126 | self.get_client().delete_egress_only_internet_gateway( 127 | EgressOnlyInternetGatewayId=self._state["egressOnlyInternetGatewayId"] 128 | ) 129 | 130 | with self.depl._db: 131 | self.state = self.MISSING 132 | self._state["region"] = None 133 | self._state["vpcId"] = None 134 | self._state["egressOnlyInternetGatewayId"] = None 135 | 136 | def destroy(self, wipe=False): 137 | self._destroy() 138 | return True 139 | -------------------------------------------------------------------------------- /nixops_aws/resources/vpc_internet_gateway.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Automatic provisioning of AWS VPC internet gateways. 4 | 5 | 6 | from nixops.state import StateDict 7 | from nixops.diff import Handler 8 | import nixops.util 9 | import nixops.resources 10 | from nixops_aws.resources.ec2_common import EC2CommonState 11 | from . import vpc, elastic_ip 12 | from .vpc import VPCState 13 | 14 | from .types.vpc_internet_gateway import VpcInternetGatewayOptions 15 | 16 | 17 | class VPCInternetGatewayDefinition(nixops.resources.ResourceDefinition): 18 | """Definition of a VPC internet gateway.""" 19 | 20 | config: VpcInternetGatewayOptions 21 | 22 | @classmethod 23 | def get_type(cls): 24 | return "vpc-internet-gateway" 25 | 26 | @classmethod 27 | def get_resource_type(cls): 28 | return "vpcInternetGateways" 29 | 30 | def show_type(self): 31 | return "{0}".format(self.get_type()) 32 | 33 | 34 | class VPCInternetGatewayState(nixops.resources.DiffEngineResourceState, EC2CommonState): 35 | """State of a VPC internet gateway.""" 36 | 37 | definition_type = VPCInternetGatewayDefinition 38 | 39 | state = nixops.util.attr_property( 40 | "state", nixops.resources.DiffEngineResourceState.MISSING, int 41 | ) 42 | access_key_id = nixops.util.attr_property("accessKeyId", None) 43 | _reserved_keys = EC2CommonState.COMMON_EC2_RESERVED + ["internetGatewayId"] 44 | 45 | def __init__(self, depl, name, id): 46 | nixops.resources.DiffEngineResourceState.__init__(self, depl, name, id) 47 | self._state = StateDict(depl, id) 48 | self.handle_create_igw = Handler( 49 | ["region", "vpcId"], handle=self.realize_create_gtw 50 | ) 51 | self.handle_tag_update = Handler( 52 | ["tags"], after=[self.handle_create_igw], handle=self.realize_update_tag 53 | ) 54 | 55 | @classmethod 56 | def get_type(cls): 57 | return "vpc-internet-gateway" 58 | 59 | def show_type(self): 60 | s = super(VPCInternetGatewayState, self).show_type() 61 | if self._state.get("region", None): 62 | s = "{0} [{1}]".format(s, self._state["region"]) 63 | return s 64 | 65 | @property 66 | def resource_id(self): 67 | return self._state.get("internetGatewayId", None) 68 | 69 | def prefix_definition(self, attr): 70 | return {("resources", "vpcInternetGateways"): attr} 71 | 72 | def get_defintion_prefix(self): 73 | return "resources.vpcInternetGateways." 74 | 75 | def create_after(self, resources, defn): 76 | return { 77 | r 78 | for r in resources 79 | if isinstance(r, vpc.VPCState) or isinstance(r, elastic_ip.ElasticIPState) 80 | } 81 | 82 | def realize_create_gtw(self, allow_recreate): 83 | config: VPCInternetGatewayDefinition = self.get_defn() 84 | 85 | if self.state == self.UP: 86 | if not allow_recreate: 87 | raise Exception( 88 | "internet gateway {} defintion changed" 89 | " use --allow-recreate if you want to create a new one".format( 90 | self._state["internetGatewayId"] 91 | ) 92 | ) 93 | self.warn("internet gateway changed, recreating...") 94 | self._destroy() 95 | 96 | self._state["region"] = config.config.region 97 | 98 | vpc_id = config.config.vpcId 99 | if vpc_id.startswith("res-"): 100 | res = self.depl.get_typed_resource( 101 | vpc_id[4:].split(".")[0], "vpc", VPCState 102 | ) 103 | vpc_id = res._state["vpcId"] 104 | 105 | self.log("creating internet gateway in region {}".format(self._state["region"])) 106 | response = self.get_client().create_internet_gateway() 107 | igw_id = response["InternetGateway"]["InternetGatewayId"] 108 | self.log("attaching internet gateway {0} to vpc {1}".format(igw_id, vpc_id)) 109 | self.get_client().attach_internet_gateway( 110 | InternetGatewayId=igw_id, VpcId=vpc_id 111 | ) 112 | with self.depl._db: 113 | self.state = self.UP 114 | self._state["region"] = config.config.region 115 | self._state["vpcId"] = vpc_id 116 | self._state["internetGatewayId"] = igw_id 117 | 118 | def realize_update_tag(self, allow_recreate): 119 | config: VPCInternetGatewayDefinition = self.get_defn() 120 | tags = {k: v for k, v in config.config.tags.items()} 121 | tags.update(self.get_common_tags()) 122 | self.get_client().create_tags( 123 | Resources=[self._state["internetGatewayId"]], 124 | Tags=[{"Key": k, "Value": tags[k]} for k in tags], 125 | ) 126 | 127 | def _destroy(self): 128 | if self.state != self.UP: 129 | return 130 | self.log( 131 | "detaching internet gateway {0} from vpc {1}".format( 132 | self._state["internetGatewayId"], self._state["vpcId"] 133 | ) 134 | ) 135 | self._retry( 136 | lambda: self.get_client().detach_internet_gateway( 137 | InternetGatewayId=self._state["internetGatewayId"], 138 | VpcId=self._state["vpcId"], 139 | ) 140 | ) 141 | self.log( 142 | "deleting internet gateway {0}".format(self._state["internetGatewayId"]) 143 | ) 144 | self.get_client().delete_internet_gateway( 145 | InternetGatewayId=self._state["internetGatewayId"] 146 | ) 147 | 148 | with self.depl._db: 149 | self.state = self.MISSING 150 | self._state["region"] = None 151 | self._state["vpcId"] = None 152 | self._state["internetGatewayId"] = None 153 | 154 | def destroy(self, wipe=False): 155 | self._destroy() 156 | return True 157 | -------------------------------------------------------------------------------- /nixops_aws/resources/vpc_route_table_association.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Automatic provisioning of AWS VPC route table association. 4 | 5 | import botocore 6 | 7 | import nixops.util 8 | import nixops.resources 9 | from nixops_aws.resources.ec2_common import EC2CommonState 10 | from nixops.diff import Handler 11 | from nixops.state import StateDict 12 | from . import vpc_route_table 13 | from .vpc_route_table import VPCRouteTableState 14 | from .vpc_subnet import VPCSubnetState 15 | 16 | from .types.vpc_route_table_association import VpcRouteTableAssociationOptions 17 | 18 | 19 | class VPCRouteTableAssociationDefinition(nixops.resources.ResourceDefinition): 20 | """Definition of a VPC route table association""" 21 | 22 | config: VpcRouteTableAssociationOptions 23 | 24 | @classmethod 25 | def get_type(cls): 26 | return "vpc-route-table-association" 27 | 28 | @classmethod 29 | def get_resource_type(cls): 30 | return "vpcRouteTableAssociations" 31 | 32 | def show_type(self): 33 | return "{0}".format(self.get_type()) 34 | 35 | 36 | class VPCRouteTableAssociationState( 37 | nixops.resources.DiffEngineResourceState, EC2CommonState 38 | ): 39 | """State of a VPC route table association""" 40 | 41 | definition_type = VPCRouteTableAssociationDefinition 42 | 43 | state = nixops.util.attr_property( 44 | "state", nixops.resources.DiffEngineResourceState.MISSING, int 45 | ) 46 | access_key_id = nixops.util.attr_property("accessKeyId", None) 47 | _reserved_keys = EC2CommonState.COMMON_EC2_RESERVED + ["associationId"] 48 | 49 | @classmethod 50 | def get_type(cls): 51 | return "vpc-route-table-association" 52 | 53 | def __init__(self, depl, name, id): 54 | nixops.resources.DiffEngineResourceState.__init__(self, depl, name, id) 55 | self._state = StateDict(depl, id) 56 | self.region = self._state.get("region", None) 57 | self.handle_associate_route_table = Handler( 58 | ["region", "routeTableId", "subnetId"], 59 | handle=self.realize_associate_route_table, 60 | ) 61 | 62 | def show_type(self): 63 | s = super(VPCRouteTableAssociationState, self).show_type() 64 | if self.region: 65 | s = "{0} [{1}]".format(s, self.region) 66 | return s 67 | 68 | @property 69 | def resource_id(self): 70 | return self._state.get("associationId", None) 71 | 72 | def prefix_definition(self, attr): 73 | return {("resources", "vpcRouteTableAssociations"): attr} 74 | 75 | def get_physical_spec(self): 76 | return {"associationId": self._state.get("associationId", None)} 77 | 78 | def get_definition_prefix(self): 79 | return "resources.vpcRouteTableAssociations." 80 | 81 | def create_after(self, resources, defn): 82 | return { 83 | r for r in resources if isinstance(r, vpc_route_table.VPCRouteTableState) 84 | } 85 | 86 | def realize_associate_route_table(self, allow_recreate): 87 | config: VPCRouteTableAssociationDefinition = self.get_defn() 88 | 89 | if self.state == self.UP: 90 | if not allow_recreate: 91 | raise Exception( 92 | "route table association {} definition changed and it needs to be recreated" 93 | " use --allow-recreate if you want to create a new one".format( 94 | self._state["associationId"] 95 | ) 96 | ) 97 | self.warn("route table association definition changed, recreating ...") 98 | self._destroy() 99 | 100 | self._state["region"] = config.config.region 101 | 102 | route_table_id = config.config.routeTableId 103 | if route_table_id.startswith("res-"): 104 | res = self.depl.get_typed_resource( 105 | route_table_id[4:].split(".")[0], "vpc-route-table", VPCRouteTableState 106 | ) 107 | route_table_id = res._state["routeTableId"] 108 | 109 | subnet_id = config.config.subnetId 110 | if subnet_id.startswith("res-"): 111 | res_vpc_subnet = self.depl.get_typed_resource( 112 | subnet_id[4:].split(".")[0], "vpc-subnet", VPCSubnetState 113 | ) 114 | subnet_id = res_vpc_subnet._state["subnetId"] 115 | 116 | self.log( 117 | "associating route table {0} to subnet {1}".format( 118 | route_table_id, subnet_id 119 | ) 120 | ) 121 | association = self.get_client().associate_route_table( 122 | RouteTableId=route_table_id, SubnetId=subnet_id 123 | ) 124 | 125 | with self.depl._db: 126 | self.state = self.UP 127 | self._state["routeTableId"] = route_table_id 128 | self._state["subnetId"] = subnet_id 129 | self._state["associationId"] = association["AssociationId"] 130 | 131 | def _destroy(self): 132 | if self.state != self.UP: 133 | return 134 | self.log( 135 | "disassociating route table {0} from subnet {1}".format( 136 | self._state["routeTableId"], self._state["subnetId"] 137 | ) 138 | ) 139 | try: 140 | self.get_client().disassociate_route_table( 141 | AssociationId=self._state["associationId"] 142 | ) 143 | except botocore.exceptions.ClientError as error: 144 | if error.response["Error"]["Code"] == "InvalidAssociationID.NotFound": 145 | self.warn( 146 | "route table {} was already deleted".format( 147 | self._state["associationId"] 148 | ) 149 | ) 150 | else: 151 | raise error 152 | 153 | with self.depl._db: 154 | self.state = self.MISSING 155 | self._state["routeTableId"] = None 156 | self._state["subnetId"] = None 157 | self._state["associationId"] = None 158 | 159 | def destroy(self, wipe=False): 160 | self._destroy() 161 | return True 162 | -------------------------------------------------------------------------------- /overrides.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: 2 | 3 | self: super: { 4 | nixops = super.nixops.overridePythonAttrs ( 5 | { nativeBuildInputs ? [], ... }: { 6 | format = "pyproject"; 7 | nativeBuildInputs = nativeBuildInputs ++ [ self.poetry ]; 8 | } 9 | ); 10 | nixos-modules-contrib = super.nixos-modules-contrib.overridePythonAttrs ( 11 | { nativeBuildInputs ? [], ... }: { 12 | format = "pyproject"; 13 | nativeBuildInputs = nativeBuildInputs ++ [ self.poetry ]; 14 | } 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "nixops_aws" 3 | version = "1.0" 4 | description = "NixOps AWS plugin" 5 | authors = ["adisbladis "] 6 | license = "LGPL-3.0-only" 7 | include = [ "nixops_aws/nix/*.nix" ] 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.7" 11 | boto = "^2.49.0" 12 | boto3 = "^1.13.7" 13 | nixops = {git = "https://github.com/NixOS/nixops.git", rev = "master"} 14 | typing-extensions = ">=3.7 <=5" 15 | nixos-modules-contrib = {git = "https://github.com/nix-community/nixos-modules-contrib.git", rev = "master"} 16 | 17 | [tool.poetry.dev-dependencies] 18 | nose = "^1.3.7" 19 | mypy = "^0.770" 20 | black = "^19.10b0" 21 | flake8 = "^3.8.2" 22 | boto3-stubs = {extras = ["ec2", "sqs", "efs", "rds"], version = "^1.13.8"} 23 | 24 | [tool.poetry.plugins."nixops"] 25 | aws = "nixops_aws.plugin" 26 | 27 | [build-system] 28 | requires = ["poetry>=0.12"] 29 | build-backend = "poetry.masonry.api" 30 | -------------------------------------------------------------------------------- /release.nix: -------------------------------------------------------------------------------- 1 | { nixpkgs ? 2 | }: 3 | 4 | let 5 | pkgs = import nixpkgs { config = {}; overlays = []; }; 6 | 7 | in rec { 8 | 9 | nixops-aws = pkgs.lib.genAttrs [ "x86_64-linux" "i686-linux" "x86_64-darwin" ] (system: 10 | let 11 | pkgs = import nixpkgs { inherit system; }; 12 | nixops-aws = import ./default.nix { inherit pkgs; }; 13 | in nixops-aws); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.7 3 | no_implicit_optional = True 4 | strict_optional = True 5 | check_untyped_defs = True 6 | 7 | [mypy-boto.*] 8 | ignore_missing_imports = True 9 | 10 | [mypy-botocore.*] 11 | ignore_missing_imports = True 12 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | let 3 | overrides = import ./overrides.nix { inherit pkgs; }; 4 | in pkgs.mkShell { 5 | buildInputs = [ 6 | (pkgs.poetry2nix.mkPoetryEnv { 7 | projectDir = ./.; 8 | overrides = pkgs.poetry2nix.overrides.withDefaults overrides; 9 | }) 10 | pkgs.poetry 11 | ]; 12 | } -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | import nose 2 | import sys 3 | 4 | if __name__ == "__main__": 5 | config = nose.config.Config(plugins=nose.plugins.manager.DefaultPluginManager()) 6 | config.configure(argv=[sys.argv[0], "-e", "^coverage-tests\.py$"] + sys.argv[1:]) 7 | count = ( 8 | nose.loader.defaultTestLoader(config=config) 9 | .loadTestsFromNames(["."]) 10 | .countTestCases() 11 | ) 12 | nose.main( 13 | argv=[ 14 | sys.argv[0], 15 | "--process-timeout=inf", 16 | "--processes=%d".format(count), 17 | "-e", 18 | "^coverage-tests\.py$", 19 | ] 20 | + sys.argv[1:] 21 | ) 22 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import threading 5 | from os import path 6 | import nixops.statefile 7 | 8 | _multiprocess_shared_ = True 9 | 10 | db_file = "%s/test.nixops" % (path.dirname(__file__)) 11 | 12 | 13 | def setup(): 14 | nixops.statefile.StateFile(db_file).close() 15 | 16 | 17 | def destroy(sf, uuid): 18 | depl = sf.open_deployment(uuid) 19 | depl.logger.set_autoresponse("y") 20 | try: 21 | depl.clean_backups(keep=0) 22 | except Exception: 23 | pass 24 | try: 25 | depl.destroy_resources() 26 | except Exception: 27 | pass 28 | depl.delete() 29 | depl.logger.log("deployment ‘{0}’ destroyed".format(uuid)) 30 | 31 | 32 | def teardown(): 33 | sf = nixops.statefile.StateFile(db_file) 34 | uuids = sf.query_deployments() 35 | threads = [] 36 | for uuid in uuids: 37 | threads.append(threading.Thread(target=destroy, args=(sf, uuid))) 38 | for thread in threads: 39 | thread.start() 40 | for thread in threads: 41 | thread.join() 42 | uuids_left = sf.query_deployments() 43 | sf.close() 44 | if not uuids_left: 45 | os.remove(db_file) 46 | else: 47 | sys.stderr.write( 48 | "warning: not all deployments have been destroyed; some resources may still exist!\n" 49 | ) 50 | -------------------------------------------------------------------------------- /tests/functional/__init__.py: -------------------------------------------------------------------------------- 1 | import nixops.statefile 2 | from tests import db_file 3 | 4 | 5 | class DatabaseUsingTest(object): 6 | _multiprocess_can_split_ = True 7 | 8 | def setup(self): 9 | self.sf = nixops.statefile.StateFile(db_file) 10 | 11 | def teardown(self): 12 | self.sf.close() 13 | -------------------------------------------------------------------------------- /tests/functional/ec2-rds-dbinstance-with-sg.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | in 4 | { 5 | network.description = "NixOps Test"; 6 | 7 | resources.rdsDbInstances.test-rds-instance = 8 | { resources, ... }: 9 | { 10 | inherit region; 11 | id = "myOtherDatabaseIsAFilesystem"; 12 | instanceClass = "db.r3.large"; 13 | allocatedStorage = 30; 14 | masterUsername = "administrator"; 15 | masterPassword = "testing123"; 16 | port = 5432; 17 | engine = "postgres"; 18 | dbName = "helloDarling"; 19 | securityGroups = [ resources.rdsDbSecurityGroups.test-rds-sg ]; 20 | }; 21 | 22 | resources.rdsDbSecurityGroups.test-rds-sg = 23 | { 24 | inherit region; 25 | groupName = "test-nixops"; 26 | description = "testing sg for rds"; 27 | rules = [ 28 | { 29 | cidrIp = "0.0.0.0/0"; 30 | } 31 | ]; 32 | }; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/functional/ec2-rds-dbinstance.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | in 4 | { 5 | network.description = "NixOps Test"; 6 | 7 | resources.rdsDbInstances.test-rds-instance = { 8 | inherit region; 9 | id = "myOtherDatabaseIsAFilesystem"; 10 | instanceClass = "db.r3.large"; 11 | allocatedStorage = 30; 12 | masterUsername = "administrator"; 13 | masterPassword = "testing123"; 14 | port = 5432; 15 | engine = "postgres"; 16 | dbName = "helloDarling"; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /tests/functional/ec2_with_nvme_device_mapping.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | zone = "us-east-1c"; 4 | in 5 | { 6 | network.description = "NixOps Test"; 7 | 8 | resources.ebsVolumes.foo-disk = { 9 | inherit region zone; 10 | size = 5; 11 | tags = { 12 | Name = "My NixOps Test Foo Disk"; 13 | }; 14 | }; 15 | 16 | resources.ec2KeyPairs.my-key-pair = 17 | { inherit region; }; 18 | 19 | resources.ec2SecurityGroups.ssh-security-group = { 20 | inherit region; 21 | description = "Security group for NixOps tests"; 22 | rules = [ { 23 | fromPort = 22; 24 | toPort = 22; 25 | sourceIp = "0.0.0.0/0"; 26 | } ]; 27 | }; 28 | 29 | machine = 30 | { resources, pkgs, ... }: 31 | { 32 | deployment.targetEnv = "ec2"; 33 | deployment.ec2 = { 34 | inherit region zone; 35 | ebsBoot = true; 36 | instanceType = "c5.large"; 37 | securityGroups = [ resources.ec2SecurityGroups.ssh-security-group ]; 38 | 39 | keyPair = resources.ec2KeyPairs.my-key-pair; 40 | }; 41 | 42 | fileSystems."/data" = { 43 | autoFormat = true; 44 | fsType = "ext4"; 45 | device = "/dev/nvme1n1"; 46 | ec2.disk = resources.ebsVolumes.foo-disk; 47 | }; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /tests/functional/single_machine_ec2_base.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | in 4 | { 5 | resources.ec2KeyPairs.my-key-pair = 6 | { inherit region; }; 7 | 8 | resources.ec2SecurityGroups.ssh-security-group = { 9 | inherit region; 10 | description = "Security group for NixOps tests"; 11 | rules = [ { 12 | fromPort = 22; 13 | toPort = 22; 14 | sourceIp = "0.0.0.0/0"; 15 | } ]; 16 | }; 17 | 18 | machine = 19 | { resources, ... }: 20 | { 21 | deployment.targetEnv = "ec2"; 22 | deployment.ec2 = { 23 | inherit region; 24 | instanceType = "c4.large"; 25 | securityGroups = [ resources.ec2SecurityGroups.ssh-security-group ]; 26 | keyPair = resources.ec2KeyPairs.my-key-pair; 27 | }; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /tests/functional/single_machine_ec2_base_nvme.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | in 4 | { 5 | resources.ec2KeyPairs.my-key-pair = 6 | { inherit region; }; 7 | 8 | resources.ec2SecurityGroups.ssh-security-group = { 9 | inherit region; 10 | description = "Security group for NixOps tests"; 11 | rules = [ { 12 | fromPort = 22; 13 | toPort = 22; 14 | sourceIp = "0.0.0.0/0"; 15 | } ]; 16 | }; 17 | 18 | machine = 19 | { resources, ... }: 20 | { 21 | deployment.targetEnv = "ec2"; 22 | deployment.ec2 = { 23 | inherit region; 24 | instanceType = "c5.large"; 25 | securityGroups = [ resources.ec2SecurityGroups.ssh-security-group ]; 26 | keyPair = resources.ec2KeyPairs.my-key-pair; 27 | }; 28 | 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /tests/functional/single_machine_ec2_ebs.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine.deployment.ec2.ebsBoot = true; 3 | } 4 | -------------------------------------------------------------------------------- /tests/functional/single_machine_ec2_raid-0-nvme.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine = { 3 | deployment.ec2.blockDeviceMapping."/dev/nvme1n1".size = 1; 4 | deployment.ec2.blockDeviceMapping."/dev/nvme2n1".size = 1; 5 | 6 | deployment.ec2.blockDeviceMapping."/dev/nvme1n1".deleteOnTermination = true; 7 | deployment.ec2.blockDeviceMapping."/dev/nvme2n1".deleteOnTermination = true; 8 | 9 | deployment.autoRaid0.raid.devices = [ "/dev/nvme1n1" "/dev/nvme2n1" ]; 10 | 11 | fileSystems."/data" = { 12 | autoFormat = true; 13 | device = "/dev/raid/raid"; 14 | fsType = "ext4"; 15 | }; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /tests/functional/single_machine_ec2_raid-0.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine = { 3 | deployment.ec2.blockDeviceMapping."/dev/xvdg".size = 1; 4 | deployment.ec2.blockDeviceMapping."/dev/xvdh".size = 1; 5 | 6 | deployment.ec2.blockDeviceMapping."/dev/xvdg".deleteOnTermination = true; 7 | deployment.ec2.blockDeviceMapping."/dev/xvdh".deleteOnTermination = true; 8 | 9 | deployment.autoRaid0.raid.devices = [ "/dev/xvdg" "/dev/xvdh" ]; 10 | 11 | fileSystems."/data" = { 12 | autoFormat = true; 13 | device = "/dev/raid/raid"; 14 | fsType = "ext4"; 15 | }; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /tests/functional/single_machine_ec2_spot_instance.nix: -------------------------------------------------------------------------------- 1 | { 2 | machine.deployment.ec2.spotInstancePrice = 24; 3 | } 4 | -------------------------------------------------------------------------------- /tests/functional/single_machine_logical_base.nix: -------------------------------------------------------------------------------- 1 | { 2 | network.description = "NixOps Test"; 3 | 4 | machine = {}; 5 | } 6 | -------------------------------------------------------------------------------- /tests/functional/test_backups.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from os import path 4 | 5 | 6 | from tests.functional import generic_deployment_test 7 | 8 | parent_dir = path.dirname(__file__) 9 | 10 | 11 | class TestBackups(generic_deployment_test.GenericDeploymentTest): 12 | _multiprocess_can_split_ = True 13 | 14 | def setup(self): 15 | super(TestBackups, self).setup() 16 | 17 | def test_simple_restore_xd_device_mapping(self): 18 | self.depl.nix_exprs = [ 19 | "%s/single_machine_logical_base.nix" % (parent_dir), 20 | "%s/single_machine_ec2_ebs.nix" % (parent_dir), 21 | "%s/single_machine_ec2_base.nix" % (parent_dir), 22 | ] 23 | self.backup_and_restore_path() 24 | 25 | def test_raid_restore_xd_device_mapping(self): 26 | self.depl.nix_exprs = [ 27 | "%s/single_machine_logical_base.nix" % (parent_dir), 28 | "%s/single_machine_ec2_ebs.nix" % (parent_dir), 29 | "%s/single_machine_ec2_base.nix" % (parent_dir), 30 | "%s/single_machine_ec2_raid-0.nix" % (parent_dir), 31 | ] 32 | self.backup_and_restore_path("/data") 33 | 34 | def test_simple_restore_on_nvme_device_mapping(self): 35 | self.depl.nix_exprs = [ 36 | "%s/single_machine_logical_base.nix" % (parent_dir), 37 | "%s/single_machine_ec2_ebs.nix" % (parent_dir), 38 | "%s/single_machine_ec2_base_nvme.nix" % (parent_dir), 39 | ] 40 | self.backup_and_restore_path() 41 | 42 | def test_raid_restore_on_nvme_device_mapping(self): 43 | self.depl.nix_exprs = [ 44 | "%s/single_machine_logical_base.nix" % (parent_dir), 45 | "%s/single_machine_ec2_ebs.nix" % (parent_dir), 46 | "%s/single_machine_ec2_base_nvme.nix" % (parent_dir), 47 | "%s/single_machine_ec2_raid-0-nvme.nix" % (parent_dir), 48 | ] 49 | self.backup_and_restore_path("/data") 50 | self.check_command("mount | grep '/dev/mapper/raid-raid on /data type ext4'") 51 | self.check_command("mount | grep '/dev/nvme0n1p1 on /'") 52 | 53 | def backup_and_restore_path(self, path=""): 54 | self.depl.deploy() 55 | self.check_command("echo -n important-data > %s/back-me-up" % (path)) 56 | backup_id = self.depl.backup() 57 | backups = self.depl.get_backups() 58 | while backups[backup_id]["status"] == "running": 59 | time.sleep(10) 60 | backups = self.depl.get_backups() 61 | self.check_command("rm %s/back-me-up" % (path)) 62 | self.depl.restore(backup_id=backup_id) 63 | self.check_command("echo -n important-data | diff %s/back-me-up -" % (path)) 64 | 65 | def check_command(self, command): 66 | self.depl.evaluate() 67 | machine = list(self.depl.machines.values())[0] 68 | return machine.run_command(command) 69 | -------------------------------------------------------------------------------- /tests/functional/test_deploys_spot_instance.py: -------------------------------------------------------------------------------- 1 | from tests.functional import single_machine_test 2 | from os import path 3 | 4 | parent_dir = path.dirname(__file__) 5 | 6 | 7 | class TestDeploysSpotInstance(single_machine_test.SingleMachineTest): 8 | def run_check(self): 9 | self.depl.nix_exprs = self.depl.nix_exprs + [ 10 | "%s/single_machine_ec2_spot_instance.nix" % (parent_dir), 11 | ] 12 | self.depl.deploy() 13 | self.check_command("test -f /etc/NIXOS") 14 | -------------------------------------------------------------------------------- /tests/functional/test_ec2_rds_dbinstance.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | 3 | 4 | from tests.functional import generic_deployment_test 5 | 6 | parent_dir = path.dirname(__file__) 7 | 8 | logical_spec = "%s/ec2-rds-dbinstance.nix" % (parent_dir) 9 | sg_spec = "%s/ec2-rds-dbinstance-with-sg.nix" % (parent_dir) 10 | 11 | 12 | class TestEc2RdsDbinstanceTest(generic_deployment_test.GenericDeploymentTest): 13 | _multiprocess_can_split_ = True 14 | 15 | def setup(self): 16 | super(TestEc2RdsDbinstanceTest, self).setup() 17 | self.depl.nix_exprs = [logical_spec] 18 | 19 | def test_deploy(self): 20 | self.depl.deploy() 21 | 22 | def test_deploy_with_sg(self): 23 | self.depl.nix_exprs = [sg_spec] 24 | self.depl.deploy() 25 | -------------------------------------------------------------------------------- /tests/functional/test_ec2_with_nvme_device_mapping.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | 3 | 4 | from tests.functional import generic_deployment_test 5 | 6 | parent_dir = path.dirname(__file__) 7 | 8 | 9 | class TestEc2WithNvmeDeviceMapping(generic_deployment_test.GenericDeploymentTest): 10 | _multiprocess_can_split_ = True 11 | 12 | def setup(self): 13 | super(TestEc2WithNvmeDeviceMapping, self).setup() 14 | 15 | def test_ec2_with_nvme_device_mapping(self): 16 | self.depl.nix_exprs = [ 17 | "%s/ec2_with_nvme_device_mapping.nix" % (parent_dir), 18 | ] 19 | self.depl.deploy() 20 | self.check_command("test -f /etc/NIXOS") 21 | self.check_command("lsblk | grep nvme1n1") 22 | self.check_command( 23 | "cat /proc/mounts | grep '/dev/nvme1n1 /data ext4 rw,relatime,data=ordered 0 0'" 24 | ) 25 | self.check_command("touch /data/asdf") 26 | 27 | def check_command(self, command): 28 | self.depl.evaluate() 29 | machine = list(self.depl.machines.values())[0] 30 | return machine.run_command(command) 31 | -------------------------------------------------------------------------------- /tests/functional/vpc.nix: -------------------------------------------------------------------------------- 1 | let 2 | region = "us-east-1"; 3 | in 4 | { 5 | resources.vpc.vpc-test = 6 | { 7 | inherit region; 8 | instanceTenancy = "default"; 9 | cidrBlock = "10.0.0.0/16"; 10 | tags = { 11 | Source = "NixOps Tests"; 12 | }; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit/test_device_name_to_boto_expected.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | from nixops.util import device_name_to_boto_expected 5 | 6 | 7 | class TestDeviceNameToBotoExpected(unittest.TestCase): 8 | def test_device_name_to_boto_expected(self): 9 | self.assertEqual( 10 | device_name_to_boto_expected("/dev/sdf"), "/dev/sdf", 11 | ) 12 | self.assertEqual(device_name_to_boto_expected("/dev/sdg"), "/dev/sdg") 13 | self.assertEqual(device_name_to_boto_expected("/dev/xvdf"), "/dev/sdf") 14 | self.assertEqual(device_name_to_boto_expected("/dev/xvdg"), "/dev/sdg") 15 | self.assertEqual(device_name_to_boto_expected("/dev/nvme1n1"), "/dev/sdf") 16 | self.assertEqual(device_name_to_boto_expected("/dev/nvme2n1"), "/dev/sdg") 17 | # TODO 18 | # self.assertEqual( 19 | # device_name_to_boto_expected('/dev/nvme26n1'), 20 | # '/dev/sdg' 21 | # ) 22 | self.assertEqual(device_name_to_boto_expected("/dev/nvme2n1p1"), "/dev/sdg1") 23 | self.assertEqual(device_name_to_boto_expected("/dev/nvme2n1p6"), "/dev/sdg6") 24 | # TODO 25 | # self.assertEqual( 26 | # device_name_to_boto_expected('/dev/nvme26n1p6'), 27 | # '/dev/sdg6' 28 | # ) 29 | --------------------------------------------------------------------------------