From 2b784c267f8688111ed3f745454e349500f426bc Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Fri, 24 May 2019 16:03:40 +0100 Subject: WIP --- .../backends/terraform_aws_controller.rb | 8 +- .../backends/terraform_aws_using_ami_controller.rb | 122 +++++++ .../backends/terraform_libvirt_controller.rb | 8 +- app/controllers/govuk_guix/revisions_controller.rb | 5 +- app/jobs/backends/terraform_aws_using_ami_job.rb | 46 +++ app/jobs/govuk_guix/fetch_revision_job.rb | 4 + app/models/backends.rb | 1 + app/models/backends/terraform_aws.rb | 2 +- app/models/backends/terraform_aws_using_ami.rb | 90 +++++ .../terraform_aws_using_ami/backend_methods.rb | 146 +++++++++ .../mini_environment_methods.rb | 113 +++++++ app/services/govuk_guix/build_mini_environment.rb | 9 +- .../govuk_guix/update_gcroots_directory.rb | 15 +- .../in_use_store_paths.html.erb | 40 +++ .../backends/terraform_aws_using_ami/new.html.erb | 192 +++++++++++ .../backends/terraform_aws_using_ami/show.html.erb | 362 +++++++++++++++++++++ app/views/layouts/unauthenticated.html.erb | 31 ++ .../unauthenticated_index.html.erb | 33 ++ config/routes.rb | 10 + .../20190519091614_terraform_aws_using_ami.rb | 17 + db/structure.sql | 57 +++- guix.scm | 8 +- lib/guix.rb | 4 + terraform/aws/backend/main.tf | 1 + terraform/aws/deploy/main.tf | 7 + .../aws_using_ami/backend/guix-daemon.service.tpl | 17 + terraform/aws_using_ami/backend/main.tf | 340 +++++++++++++++++++ terraform/aws_using_ami/deploy/main.tf | 7 + .../mini_environment/govuk.service.tpl | 12 + terraform/aws_using_ami/mini_environment/main.tf | 164 ++++++++++ 30 files changed, 1854 insertions(+), 17 deletions(-) create mode 100644 app/controllers/backends/terraform_aws_using_ami_controller.rb create mode 100644 app/jobs/backends/terraform_aws_using_ami_job.rb create mode 100644 app/models/backends/terraform_aws_using_ami.rb create mode 100644 app/models/backends/terraform_aws_using_ami/backend_methods.rb create mode 100644 app/models/backends/terraform_aws_using_ami/mini_environment_methods.rb create mode 100644 app/views/backends/terraform_aws_using_ami/in_use_store_paths.html.erb create mode 100644 app/views/backends/terraform_aws_using_ami/new.html.erb create mode 100644 app/views/backends/terraform_aws_using_ami/show.html.erb create mode 100644 app/views/layouts/unauthenticated.html.erb create mode 100644 app/views/mini_environments/unauthenticated_index.html.erb create mode 100644 db/migrate/20190519091614_terraform_aws_using_ami.rb create mode 100644 terraform/aws/deploy/main.tf create mode 100644 terraform/aws_using_ami/backend/guix-daemon.service.tpl create mode 100644 terraform/aws_using_ami/backend/main.tf create mode 100644 terraform/aws_using_ami/deploy/main.tf create mode 100644 terraform/aws_using_ami/mini_environment/govuk.service.tpl create mode 100644 terraform/aws_using_ami/mini_environment/main.tf diff --git a/app/controllers/backends/terraform_aws_controller.rb b/app/controllers/backends/terraform_aws_controller.rb index b09001d..3255ebe 100644 --- a/app/controllers/backends/terraform_aws_controller.rb +++ b/app/controllers/backends/terraform_aws_controller.rb @@ -49,8 +49,12 @@ class Backends::TerraformAwsController < ApplicationController def destroy backend = Backends::TerraformAws.find(params[:id]) - flash[:success] = "Backend #{backend.label} deleted" - backend.delete + if @backend.mini_environments.empty? + flash[:success] = "Backend #{backend.label} deleted" + backend.delete + else + flash[:error] = "Unable to delete backend, as mini environments using this backend still exist." + end redirect_to setup_path end diff --git a/app/controllers/backends/terraform_aws_using_ami_controller.rb b/app/controllers/backends/terraform_aws_using_ami_controller.rb new file mode 100644 index 0000000..f109251 --- /dev/null +++ b/app/controllers/backends/terraform_aws_using_ami_controller.rb @@ -0,0 +1,122 @@ +# GOV.UK Mini Environment Admin +# Copyright © 2018, 2019 Christopher Baines +# +# This file is part of the GOV.UK Mini Environment Admin. +# +# The GOV.UK Mini Environment Admin is free software: you can +# redistribute it and/or modify it under the terms of the GNU Affero +# General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later +# version. +# +# The GOV.UK Mini Environment Admin is distributed in the hope that it +# will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with the GOV.UK Mini Environment Admin. If not, see +# . + +class Backends::TerraformAwsUsingAmiController < ApplicationController + def new + @backend = Backends::TerraformAws.new + end + + def create + backend = Backends::TerraformAwsUsingAmi.create(create_params) + + flash[:success] = "Backend #{backend.label} created" + + redirect_to terraform_aws_using_ami_backend_path(backend) + end + + def update + @backend = Backends::TerraformAwsUsingAmi.update( + params[:id], + update_params + ) + + flash[:success] = "Backend #{@backend.label} updated" + + render :show + end + + def show + @backend = Backends::TerraformAwsUsingAmi.find(params[:id]) + end + + def destroy + backend = Backends::TerraformAwsUsingAmi.find(params[:id]) + + if @backend.mini_environments.empty? + flash[:success] = "Backend #{backend.label} deleted" + backend.delete + else + flash[:error] = "Unable to delete backend, as mini environments using this backend still exist." + end + + redirect_to setup_path + end + + def perform_action + @backend = Backends::TerraformAwsUsingAmi.find(params['id']) + + action = params.require(:commit) + + case action + when 'Destroy' + Backends::TerraformAwsUsingAmiJob.enqueue(@backend.id, :destroy_backend) + + flash[:notice] = 'Destroying the backend' + when 'Stop' + Backends::TerraformAwsUsingAmiJob.enqueue(@backend.id, :stop_backend) + + flash[:notice] = 'Stopping the backend' + when 'Deploy' + Backends::TerraformAwsUsingAmiJob.enqueue(@backend.id, :deploy_backend) + + flash[:notice] = 'Deploying the backend' + when 'Refresh state' + Backends::TerraformAwsUsingAmiJob.enqueue(@backend.id, :refresh_backend_state) + + flash[:notice] = 'Refreshing the backend state information' + else + flash[:error] = "Unknown action #{action}" + end + + redirect_to terraform_aws_using_ami_backend_path(@backend) + end + + def in_use_store_paths + @backend = Backends::TerraformAwsUsingAmi.find(params['id']) + end + + private + + def create_params + params + .require(:backends_terraform_aws) + .permit( + :label, + :domain, + :aws_region, + :vpc_id, + :route_53_zone_id, + :aws_access_key_id, + :aws_secret_access_key, + :ssh_public_key, + :ssh_private_key + ) + end + + def update_params + params + .require(:backends_terraform_aws) + .permit( + :label, + :aws_access_key_id, + :aws_secret_access_key + ) + end +end diff --git a/app/controllers/backends/terraform_libvirt_controller.rb b/app/controllers/backends/terraform_libvirt_controller.rb index 1bf37cd..8bc7f2c 100644 --- a/app/controllers/backends/terraform_libvirt_controller.rb +++ b/app/controllers/backends/terraform_libvirt_controller.rb @@ -51,8 +51,12 @@ class Backends::TerraformLibvirtController < ApplicationController def destroy backend = Backends::TerraformLibvirt.find(params['id']) - flash[:success] = "Backend #{backend.label} deleted" - backend.delete + if @backend.mini_environments.empty? + flash[:success] = "Backend #{backend.label} deleted" + backend.delete + else + flash[:error] = "Unable to delete backend, as mini environments using this backend still exist." + end redirect_to setup_path end diff --git a/app/controllers/govuk_guix/revisions_controller.rb b/app/controllers/govuk_guix/revisions_controller.rb index e4e1b52..ad37009 100644 --- a/app/controllers/govuk_guix/revisions_controller.rb +++ b/app/controllers/govuk_guix/revisions_controller.rb @@ -36,11 +36,12 @@ class GovukGuix::RevisionsController < ApplicationController revision = params.require('revision') # Attempt to check if this can be performed locally - if Guix.available_locally? + if Guix.available_locally? && false # TODO options = {} else # Assume that the AWS backend is in use - backend = Backends::TerraformAws.first + backend = Backends::TerraformAwsUsingAmi.first || + Backends::TerraformAws.first options = { backend_type_and_id: backend.type_and_id diff --git a/app/jobs/backends/terraform_aws_using_ami_job.rb b/app/jobs/backends/terraform_aws_using_ami_job.rb new file mode 100644 index 0000000..1412e8c --- /dev/null +++ b/app/jobs/backends/terraform_aws_using_ami_job.rb @@ -0,0 +1,46 @@ +# GOV.UK Mini Environment Admin +# Copyright © 2018, 2019 Christopher Baines +# +# This file is part of the GOV.UK Mini Environment Admin. +# +# The GOV.UK Mini Environment Admin is free software: you can +# redistribute it and/or modify it under the terms of the GNU Affero +# General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later +# version. +# +# The GOV.UK Mini Environment Admin is distributed in the hope that it +# will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with the GOV.UK Mini Environment Admin. If not, see +# . + +class Backends::TerraformAwsUsingAmiJob < Que::Job + def run(terraform_aws_using_ami_backend_id, action) + ActiveRecord::Base.transaction do + @backend = Backends::TerraformAwsUsingAmi.find( + terraform_aws_using_ami_backend_id + ) + + @backend.send(action) + + finish + end + end + + def self.job_title(que_job) + que_job.args.last.titleize + end + + def self.jobs(terraform_aws_using_ami_backend_id) + QueJob + .where( + job_class: name + ).where( + "args->>0 = '#{terraform_aws_using_ami_backend_id}'" + ) + end +end diff --git a/app/jobs/govuk_guix/fetch_revision_job.rb b/app/jobs/govuk_guix/fetch_revision_job.rb index 7b1acdf..0265c6e 100644 --- a/app/jobs/govuk_guix/fetch_revision_job.rb +++ b/app/jobs/govuk_guix/fetch_revision_job.rb @@ -53,6 +53,10 @@ class GovukGuix::FetchRevisionJob < Que::Job store_path = output.last.strip logger.debug(self.class) { "store_path: #{store_path}" } + unless Guix.valid_store_path? store_path + raise "Invalid store path: #{store_path}" + end + backend.add_in_use_store_path(store_path) if backend GovukGuix::Revision.transaction do diff --git a/app/models/backends.rb b/app/models/backends.rb index 526ae76..5f812d7 100644 --- a/app/models/backends.rb +++ b/app/models/backends.rb @@ -26,6 +26,7 @@ module Backends def self.classes [ Backends::TerraformAws, + Backends::TerraformAwsUsingAmi, Backends::TerraformLibvirt ] end diff --git a/app/models/backends/terraform_aws.rb b/app/models/backends/terraform_aws.rb index d3d56d1..a19bf75 100644 --- a/app/models/backends/terraform_aws.rb +++ b/app/models/backends/terraform_aws.rb @@ -47,7 +47,7 @@ class Backends::TerraformAws < ApplicationRecord self.table_name = 'terraform_aws_backends' def self.label - 'Amazon Web Services' + 'Amazon Web Services (using EFS)' end def self.available? diff --git a/app/models/backends/terraform_aws_using_ami.rb b/app/models/backends/terraform_aws_using_ami.rb new file mode 100644 index 0000000..bb9f90d --- /dev/null +++ b/app/models/backends/terraform_aws_using_ami.rb @@ -0,0 +1,90 @@ +# GOV.UK Mini Environment Admin +# Copyright © 2018, 2019 Christopher Baines +# +# This file is part of the GOV.UK Mini Environment Admin. +# +# The GOV.UK Mini Environment Admin is free software: you can +# redistribute it and/or modify it under the terms of the GNU Affero +# General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later +# version. +# +# The GOV.UK Mini Environment Admin is distributed in the hope that it +# will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with the GOV.UK Mini Environment Admin. If not, see +# . + +# == Schema Information +# +# Table name: terraform_aws_backends +# +# id :integer not null, primary key +# label :string +# aws_region :string +# aws_access_key_id :string +# aws_secret_access_key :string +# created_at :datetime not null +# updated_at :datetime not null +# domain :string +# route_53_zone_id :string not null +# vpc_id :string not null +# ssh_public_key :string +# ssh_private_key :string +# + +require 'ruby_terraform' + +class Backends::TerraformAwsUsingAmi < ApplicationRecord + include MiniEnvironmentMethods + include BackendMethods + + has_many :mini_environments, as: :backend + + self.table_name = 'terraform_aws_using_ami_backends' + + def self.label + 'Amazon Web Services (using AMIs)' + end + + def self.available? + File.exist? "#{ENV['PATH'].split(':').first}/terraform-provider-aws" + end + + def type_and_id + "#{self.class.name}=#{id}" + end + + def common_terraform_variables + { + aws_access_key: aws_access_key_id, + aws_secret_key: aws_secret_access_key, + aws_region: aws_region, + ssh_private_key: ssh_private_key, + aws_route_53_zone_id: route_53_zone_id + } + end + + def build_remote_host + RemoteHost.new( + 'ubuntu', + backend_latest_terraform_state.output_value('guix_daemon_public_dns'), + ssh_private_key + ) + end + + def terraform_state_id + "backend/terraform_aws_using_ami/#{id}" + end + + def guix_public_key + "(entry #{File.read("/etc/guix/signing-key.pub")} (tag (guix import)))" + rescue Errno::ENOENT + # This is optional, as if it doesn't exist, it means that `guix + # copy` won't be used + '' + end +end diff --git a/app/models/backends/terraform_aws_using_ami/backend_methods.rb b/app/models/backends/terraform_aws_using_ami/backend_methods.rb new file mode 100644 index 0000000..10bf872 --- /dev/null +++ b/app/models/backends/terraform_aws_using_ami/backend_methods.rb @@ -0,0 +1,146 @@ +# GOV.UK Mini Environment Admin +# Copyright © 2018, 2019 Christopher Baines +# +# This file is part of the GOV.UK Mini Environment Admin. +# +# The GOV.UK Mini Environment Admin is free software: you can +# redistribute it and/or modify it under the terms of the GNU Affero +# General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later +# version. +# +# The GOV.UK Mini Environment Admin is distributed in the hope that it +# will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with the GOV.UK Mini Environment Admin. If not, see +# . + +module Backends::TerraformAwsUsingAmi::BackendMethods + def create_data_snapshot + GovukGuix::CreateDataSnapshotJob.enqueue( + backend_type: self.class.name, + backend_id: id + ) + end + + def backend_terraform_variables + public_ip_addresses = ENV[ + 'GOVUK_MINI_ENVIRONMENT_ADMIN_PUBLIC_IP_ADDRESSES' + ].split(',') + + raise 'missing public ip addresses' if public_ip_addresses.nil? + + egress_cidr_blocks = public_ip_addresses.map { |x| "#{x}/32" } + + common_terraform_variables.merge( + aws_vpc_id: vpc_id, + ssh_public_key: ssh_public_key, + backend_slug: label.parameterize, + mini_environment_admin_guix_public_key: guix_public_key, + mini_environment_admin_egress_cidr_blocks: egress_cidr_blocks + ) + end + + def deploy_backend + within_backend_terraform_working_directory do + RubyTerraform.apply( + vars: backend_terraform_variables, + auto_approve: true + ) + end + end + + def refresh_backend_state + within_backend_terraform_working_directory do + RubyTerraform.refresh( + vars: backend_terraform_variables + ) + end + end + + def destroy_backend + within_backend_terraform_working_directory do + RubyTerraform.destroy( + vars: backend_terraform_variables, + force: true + ) + end + end + + def stop_backend + within_backend_terraform_working_directory do + RubyTerraform.destroy( + vars: backend_terraform_variables, + target: 'aws_spot_instance_request.main', + force: true + ) + end + end + + def in_use_store_paths + [ + GovukGuix::Revision.where(archived: false).pluck(:store_path), + available_data_snapshots.pluck(:store_path), + mini_environments + .where(archived: false) + .pluck(:backend_data) + .map { |x| x&.dig('build_output') } + ].flatten.compact + end + + def update_guix_gcroots + GovukGuix::UpdateGcrootsDirectory.set_in_use_store_paths( + in_use_store_paths, + run_remotely_on_host: build_remote_host + ) + end + + def add_in_use_store_path(store_path) + GovukGuix::UpdateGcrootsDirectory.add_store_path( + store_path, + run_remotely_on_host: build_remote_host + ) + end + + def within_backend_terraform_working_directory(&block) + with_advisory_lock( + "terraform" + ) do + TerraformWorkingDirectory.new( + terraform_state_id, + 'terraform/aws_using_ami/backend' + ).within_working_directory(&block) + end + end + + def available_data_snapshots + GovukGuix::DataSnapshot.where(backend: self) + end + + def backend_terraform_states + TerraformState.where( + state_id: terraform_state_id + ) + end + + def backend_latest_terraform_state + backend_terraform_states.order(:id).last + end + + def status + latest_terraform_state = backend_terraform_states.order(:id).last + + running = ( + latest_terraform_state && + (latest_terraform_state.output_value('backend_up') == 'true') + ) + + { + running: running, + updated_at: latest_terraform_state.try(:created_at) + } + end +end diff --git a/app/models/backends/terraform_aws_using_ami/mini_environment_methods.rb b/app/models/backends/terraform_aws_using_ami/mini_environment_methods.rb new file mode 100644 index 0000000..66a3cbc --- /dev/null +++ b/app/models/backends/terraform_aws_using_ami/mini_environment_methods.rb @@ -0,0 +1,113 @@ +# GOV.UK Mini Environment Admin +# Copyright © 2018, 2019 Christopher Baines +# +# This file is part of the GOV.UK Mini Environment Admin. +# +# The GOV.UK Mini Environment Admin is free software: you can +# redistribute it and/or modify it under the terms of the GNU Affero +# General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later +# version. +# +# The GOV.UK Mini Environment Admin is distributed in the hope that it +# will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with the GOV.UK Mini Environment Admin. If not, see +# . + +module Backends::TerraformAwsUsingAmi::MiniEnvironmentMethods + def build(mini_environment) + slug = mini_environment.name.parameterize + + GovukGuix::BuildMiniEnvironment.build( + mini_environment.id, + services: mini_environment.services.map(&:build_argument_string), + arguments: { + type: 'aws-packer-ami', + app_domain: "#{slug}.#{domain}", + web_domain: "www.#{slug}.#{domain}", + use_https: 'certbot', + signon_instance_name: slug, + admin_environment_label: mini_environment.name, + use_error_pages: 'true', + origin_basic_auth: "#{slug}=#{slug}", + }, + run_remotely_on_host: mini_environment.backend.build_remote_host + ) + end + + def start(mini_environment) + logger.info "Setting up #{mini_environment.name}" + + within_terraform_working_directory(mini_environment) do + RubyTerraform.apply( + vars: terraform_variables(mini_environment), + auto_approve: true + ) + end + end + + def destroy(mini_environment) + within_terraform_working_directory(mini_environment) do + RubyTerraform.destroy( + vars: terraform_variables(mini_environment), + force: true + ) + end + end + + def refresh(mini_environment) + within_terraform_working_directory(mini_environment) do + RubyTerraform.refresh( + vars: terraform_variables(mini_environment) + ) + end + end + + def terraform_states(mini_environment) + TerraformState.where( + state_id: mini_environment_state_id(mini_environment) + ) + end + + def within_terraform_working_directory(mini_environment, &block) + with_advisory_lock( + "terraform" + ) do + TerraformWorkingDirectory.new( + mini_environment_state_id(mini_environment), + 'terraform/aws/mini_environment' + ).within_working_directory(&block) + end + end + + def mini_environment_state_id(mini_environment) + "mini_environment/#{mini_environment.id}" + end + + def signon_url(mini_environment) + "https://signon.#{mini_environment.name.parameterize}.#{domain}" + end + + def terraform_variables(mini_environment) + credentials = TerraformHttpBackendController.credentials + + common_terraform_variables.merge( + slug: mini_environment.name.parameterize, + ami_id: mini_environment.backend_data['build_output'], + backend_remote_state_address: ( + Plek.new.external_url_for('mini-environment-admin') + + Rails + .application + .routes + .url_helpers + .terraform_http_backend_path(terraform_state_id) + ), + backend_remote_state_username: credentials[:name], + backend_remote_state_password: credentials[:password] + ) + end +end diff --git a/app/services/govuk_guix/build_mini_environment.rb b/app/services/govuk_guix/build_mini_environment.rb index 9cada20..37fc16b 100644 --- a/app/services/govuk_guix/build_mini_environment.rb +++ b/app/services/govuk_guix/build_mini_environment.rb @@ -36,7 +36,7 @@ module GovukGuix::BuildMiniEnvironment remote_host = options[:run_remotely_on_host] - if remote_host && Guix.available_locally? + if remote_host && Guix.available_locally? && false # TODO # TODO: This doesn't use the private key specified by the # backend, so it'll only work when the default SSH key has # access to the remote host. @@ -73,11 +73,12 @@ module GovukGuix::BuildMiniEnvironment ) build_output = output.last.strip - - raise 'InvalidOutput' unless build_output.starts_with? '/gnu/store' - logger.debug(self.class) { "build_output: #{build_output}" } + unless arguments[:type] == 'aws-packer-ami' + raise 'InvalidOutput' unless build_output.starts_with? '/gnu/store' + end + mini_environment.update( backend_data: { build_output: build_output diff --git a/app/services/govuk_guix/update_gcroots_directory.rb b/app/services/govuk_guix/update_gcroots_directory.rb index 97fe703..dec4679 100644 --- a/app/services/govuk_guix/update_gcroots_directory.rb +++ b/app/services/govuk_guix/update_gcroots_directory.rb @@ -24,6 +24,11 @@ module GovukGuix::UpdateGcrootsDirectory DIRECTORY = '/var/guix/gcroots/govuk-mini-environment-admin' def self.set_in_use_store_paths(store_paths, options = {}) + store_paths.each do |store_path| + raise "Invalid store path #{store_path}" \ + unless Guix.valid_store_path?(store_path) + end + current_store_paths = list_store_paths(options) (current_store_paths - store_paths).each do |store_path| @@ -36,15 +41,21 @@ module GovukGuix::UpdateGcrootsDirectory end def self.add_store_path(store_path, options = {}) + raise "Invalid store path #{store_path}" \ + unless Guix.valid_store_path?(store_path) + run_command( - 'ln', '-s', - store_path, + 'ln', '-f', '-s', + '-T', store_path, File.join(DIRECTORY, store_path['/gnu/store/'.length..-1]), run_remotely_on_host: options[:run_remotely_on_host] ) end def self.remove_store_path(store_path, options = {}) + raise "Invalid store path #{store_path}" \ + unless Guix.valid_store_path?(store_path) + run_command( 'rm', File.join(DIRECTORY, store_path['/gnu/store/'.length..-1]), run_remotely_on_host: options[:run_remotely_on_host] diff --git a/app/views/backends/terraform_aws_using_ami/in_use_store_paths.html.erb b/app/views/backends/terraform_aws_using_ami/in_use_store_paths.html.erb new file mode 100644 index 0000000..c3b694c --- /dev/null +++ b/app/views/backends/terraform_aws_using_ami/in_use_store_paths.html.erb @@ -0,0 +1,40 @@ +<%# + +GOV.UK Mini Environment Admin +Copyright © 2018 Christopher Baines + +This file is part of the GOV.UK Mini Environment Admin. + +The GOV.UK Mini Environment Admin is free software: you can +redistribute it and/or modify it under the terms of the GNU Affero +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later +version. + +The GOV.UK Mini Environment Admin is distributed in the hope that it +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public +License along with the GOV.UK Mini Environment Admin. If not, see +. + +%> + +

Backend: <%= @backend.label %>

+<% status = @backend.status %> + +
+ +

In use store paths

+ + + <% @backend.in_use_store_paths.each do |store_path| %> + + + + + <% end %> +
<%= store_path %> +
diff --git a/app/views/backends/terraform_aws_using_ami/new.html.erb b/app/views/backends/terraform_aws_using_ami/new.html.erb new file mode 100644 index 0000000..790e5eb --- /dev/null +++ b/app/views/backends/terraform_aws_using_ami/new.html.erb @@ -0,0 +1,192 @@ +<%# + +GOV.UK Mini Environment Admin +Copyright © 2018 Christopher Baines + +This file is part of the GOV.UK Mini Environment Admin. + +The GOV.UK Mini Environment Admin is free software: you can +redistribute it and/or modify it under the terms of the GNU Affero +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later +version. + +The GOV.UK Mini Environment Admin is distributed in the hope that it +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public +License along with the GOV.UK Mini Environment Admin. If not, see +. + +%> + + + Back to setup + + +

Create a new AWS backend

+ +
+
+ <%= form_with(model: @backend, + url: { action: "create" }, + html: { class: "form-horizontal" }) do |f| + %> + +
+ <%= f.label :label, class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :label, + class: 'form-control', + placeholder: 'Label for this backend' + ) %> +
+
+ +
+ <%= f.label :domain, class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :domain, + class: 'form-control', + placeholder: 'Domain within which to host mini environments' + ) %> + +

+ For example, if you entered example.com for + the backend domain, and then created a mini environment + called "Test" using this backend, then the application + domain for the mini environment would be + test.example.com. +

+

+ A Route53 Hosted Zone will be created for this domain, + and records added for the mini environments. +

+
+
+
+ +
+ <%= f.label :aws_region, 'AWS Region', class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :aws_region, + class: 'form-control', + placeholder: 'What region to use' + ) %> +
+
+ +
+ <%= f.label( + :vpc_id, + 'VPC ID', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_field( + :vpc_id, + class: 'form-control', + placeholder: 'The ID of the VPC (Virtual Private Cloud) to use' + ) %> + +

+ This VPC (Virtual Private Cloud) should be the one to + use for all resources. +

+
+
+
+ +
+ <%= f.label( + :route_53_zone_id, + 'Route 53 Zone ID', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_field( + :route_53_zone_id, + class: 'form-control', + placeholder: 'The ID of the Route 53 Zone to use' + ) %> + +

+ This zone should be authoritive for the domain this + backend is using. Entries in this zone will be created + for the mini environments. +

+
+
+
+ +
+ <%= f.label :aws_access_key_id, 'AWS Access Key ID', class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :aws_access_key_id, + class: 'form-control', + ) %> +
+
+ +
+ <%= f.label :aws_secret_access_key, 'AWS Secret Access Key', class: 'col-sm-4 control-label' %> +
+ <%= f.password_field( + :aws_secret_access_key, + class: 'form-control', + ) %> +
+
+ +
+ <%= f.label( + :ssh_public_key, + 'SSH Key, public part', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_area( + :ssh_public_key, + class: 'form-control', + placeholder: 'The public part of the SSH key to use' + ) %> + +

+

+
+
+
+ +
+ <%= f.label( + :ssh_private_key, + 'SSH Key, private part', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_area( + :ssh_private_key, + class: 'form-control', + placeholder: 'The private part of the SSH key to use' + ) %> + +

+

+
+
+
+ +
+
+ <%= f.submit "Create", class: 'btn btn-lg btn-success' %> +
+
+ <% end %> +
+
diff --git a/app/views/backends/terraform_aws_using_ami/show.html.erb b/app/views/backends/terraform_aws_using_ami/show.html.erb new file mode 100644 index 0000000..f9623b2 --- /dev/null +++ b/app/views/backends/terraform_aws_using_ami/show.html.erb @@ -0,0 +1,362 @@ +<%# + +GOV.UK Mini Environment Admin +Copyright © 2018 Christopher Baines + +This file is part of the GOV.UK Mini Environment Admin. + +The GOV.UK Mini Environment Admin is free software: you can +redistribute it and/or modify it under the terms of the GNU Affero +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later +version. + +The GOV.UK Mini Environment Admin is distributed in the hope that it +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public +License along with the GOV.UK Mini Environment Admin. If not, see +. + +%> + + + Back to Setup + + +

Backend: <%= @backend.label %>

+<% status = @backend.status %> + +
+ +
+
+ +
+
+ Current Status
+ updated at <%= status[:updated_at] %> +
+
+ <% if status[:running] %> + + <% else %> + + <% end %> + +
+ + View Terraform state information + +
+
+ + <%= form_with( + url: perform_action_terraform_aws_using_ami_backend_path(@backend), + local: true, + method: "post" + ) do %> + +
+
Actions
+ +
    +
  • + <%= submit_tag('Deploy', + role: 'button', + style: 'margin-bottom: 5px;', + class: 'btn btn-lg btn-success btn-block') + %> +

    + Run Terraform to deploy this backend, ensuring everything + is setup to create new mini environments. +

    +
  • +
  • + <%= submit_tag("Stop", + role: 'button', + style: 'margin-bottom: 5px;', + class: 'btn btn-lg btn-warning btn-block') + %> +

    + Run Terraform to stop this backend, terminating the + build machine used to build new mini + environments. Existing environments will continue to + run, but new environments cannot be created. +

    +
  • +
  • + <%= submit_tag("Destroy", + role: 'button', + style: 'margin-bottom: 5px;', + class: 'btn btn-lg btn-danger btn-block') + %> +

    + Run Terraform to destroy this backend, note that this will + remove all cached data and disrupt all mini environments + using this backend. +

    +
  • +
  • + <%= submit_tag("Refresh state", + role: 'button', + style: 'margin-bottom: 5px;', + class: 'btn btn-lg btn-info btn-block') + %> +

    + Run Terraform to refresh the state information for this + backend. +

    +
  • +
+
+ <% end %> +
+
+ <%= render( + partial: 'shared/jobs', + locals: { + jobs: Backends::TerraformAwsJob.jobs(@backend.id).order(id: :desc) + } + ) %> +
+
+ +
+
+

Update details

+
+ + <%= form_with(model: @backend, + url: { action: "update" }, + html: { class: "form-horizontal" }) do |f| + %> + +
+ <%= f.label :label, class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :label, + class: 'form-control', + placeholder: 'Label for this backend' + ) %> +
+
+ +
+ <%= f.label :domain, class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :domain, + class: 'form-control', + placeholder: 'Domain within which to host mini environments', + readonly: true + ) %> + +

+ For example, if you entered example.com for + the backend domain, and then created a mini environment + called "Test" using this backend, then the application + domain for the mini environment would be + test.example.com. +

+

+ A Route53 Hosted Zone will be created for this domain, + and records added for the mini environments. +

+
+
+
+ +
+ <%= f.label :aws_region, 'AWS Region', class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :aws_region, + class: 'form-control', + placeholder: 'What region to use', + readonly: true, + ) %> +
+
+ +
+ <%= f.label( + :vpc_id, + 'VPC ID', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_field( + :vpc_id, + class: 'form-control', + placeholder: 'The ID of the VPC (Virtual Private Cloud) to use', + readonly: true + ) %> + +

+ This VPC (Virtual Private Cloud) should be the one to + use for all resources. +

+
+
+
+ +
+ <%= f.label( + :route_53_zone_id, + 'Route 53 Zone ID', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_field( + :route_53_zone_id, + class: 'form-control', + placeholder: 'The ID of the Route 53 Zone to use', + readonly: true + ) %> + +

+ This zone should be authoritive for the domain this + backend is using. Entries in this zone will be created + for the mini environments. +

+
+
+
+ +
+ <%= f.label :aws_access_key_id, 'AWS Access Key ID', class: 'col-sm-4 control-label' %> +
+ <%= f.text_field( + :aws_access_key_id, + class: 'form-control', + ) %> +
+
+ +
+ <%= f.label :aws_secret_access_key, 'AWS Secret Access Key', class: 'col-sm-4 control-label' %> +
+ <%= f.password_field( + :aws_secret_access_key, + class: 'form-control', + placeholder: 'Secret key hidden', + ) %> + + The AWS Secret Access Key is not accessible once entered. + +
+
+ +
+ <%= f.label( + :ssh_public_key, + 'SSH Key, public part', + class: 'col-sm-4 control-label' + ) %> +
+ <%= f.text_area( + :ssh_public_key, + class: 'form-control', + placeholder: 'The public part of the SSH key to use', + readonly: true + ) %> + +

+

+
+
+
+ +
+ <%= f.label( + :ssh_private_key, + 'SSH Key, private part', + class: 'col-sm-4 control-label' + ) %> +
+ <%= text_area_tag( + :ssh_private_key, + 'Secret key hidden', + class: 'form-control', + disabled: true + ) %> + +

+

+
+
+
+ +
+
+ <%= f.submit "Save", class: 'btn btn-lg btn-success' %> +
+
+ <% end %> +
+
+

Delete backend

+
+ + <% unless @backend.mini_environments.empty? %> +

+ Unable to delete backend, as mini environments using this + backend still exist. +

+ +

+ To delete this backend, first delete all the mini environments + using it. +

+ <% end %> + + <%= form_with(model: @backend, + url: { action: "destroy" }, + html: { class: "form-horizontal", method: :delete }) do |f| + %> + <%= f.submit( + "Delete", + class: ( + 'btn btn-lg btn-danger' + + (@backend.mini_environments.empty? ? '' : ' disabled') + ) + ) %> + <% end %> + +
+
+ +

Mini environments

+ + + + + + + <% @backend.mini_environments.each do |mini_environment| %> + + + + + <% end %> +
Name
<%= mini_environment.name %> + + Show details + +
diff --git a/app/views/layouts/unauthenticated.html.erb b/app/views/layouts/unauthenticated.html.erb new file mode 100644 index 0000000..b4975f1 --- /dev/null +++ b/app/views/layouts/unauthenticated.html.erb @@ -0,0 +1,31 @@ +<%# + +GOV.UK Mini Environment Admin +Copyright © 2018 Christopher Baines + +This file is part of the GOV.UK Mini Environment Admin. + +The GOV.UK Mini Environment Admin is free software: you can +redistribute it and/or modify it under the terms of the GNU Affero +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later +version. + +The GOV.UK Mini Environment Admin is distributed in the hope that it +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public +License along with the GOV.UK Mini Environment Admin. If not, see +. + +%> + +<% content_for :head do %> + <%= stylesheet_link_tag "application", :media => "all" %> + <%= csrf_meta_tag %> + <%= yield :extra_headers %> +<% end %> + +<%= render template: 'layouts/govuk_admin_template' %> diff --git a/app/views/mini_environments/unauthenticated_index.html.erb b/app/views/mini_environments/unauthenticated_index.html.erb new file mode 100644 index 0000000..8241590 --- /dev/null +++ b/app/views/mini_environments/unauthenticated_index.html.erb @@ -0,0 +1,33 @@ +<%# + +GOV.UK Mini Environment Admin +Copyright © 2018 Christopher Baines + +This file is part of the GOV.UK Mini Environment Admin. + +The GOV.UK Mini Environment Admin is free software: you can +redistribute it and/or modify it under the terms of the GNU Affero +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later +version. + +The GOV.UK Mini Environment Admin is distributed in the hope that it +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public +License along with the GOV.UK Mini Environment Admin. If not, see +. + +%> + +
+

Quickly and easily create standalone GOV.UK environments

+

...

+

+ + Sign in + +

+
diff --git a/config/routes.rb b/config/routes.rb index 8242395..50a37cf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,6 +23,16 @@ Rails.application.routes.draw do end end + resources :terraform_aws_using_ami, + as: 'terraform_aws_using_ami_backends', + controller: 'backends/terraform_aws_using_ami', + only: %i[create new show update destroy] do + member do + post 'perform_action' + get 'in_use_store_paths' + end + end + resources :terraform_libvirt, as: 'terraform_libvirt_backends', controller: 'backends/terraform_libvirt', diff --git a/db/migrate/20190519091614_terraform_aws_using_ami.rb b/db/migrate/20190519091614_terraform_aws_using_ami.rb new file mode 100644 index 0000000..0375a2a --- /dev/null +++ b/db/migrate/20190519091614_terraform_aws_using_ami.rb @@ -0,0 +1,17 @@ +class TerraformAwsUsingAmi < ActiveRecord::Migration[5.2] + def change + create_table :terraform_aws_using_ami_backends do |t| + t.string :label + t.string :aws_region + t.string :aws_access_key_id + t.string :aws_secret_access_key + t.string :domain + t.string :route_53_zone_id, null: false + t.string :vpc_id, null: false + t.string :ssh_public_key + t.string :ssh_private_key + + t.timestamps + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 5e5aaba..7fd8ca2 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -458,6 +458,45 @@ CREATE SEQUENCE public.terraform_aws_backends_id_seq ALTER SEQUENCE public.terraform_aws_backends_id_seq OWNED BY public.terraform_aws_backends.id; +-- +-- Name: terraform_aws_using_ami_backends; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.terraform_aws_using_ami_backends ( + id bigint NOT NULL, + label character varying, + aws_region character varying, + aws_access_key_id character varying, + aws_secret_access_key character varying, + domain character varying, + route_53_zone_id character varying NOT NULL, + vpc_id character varying NOT NULL, + ssh_public_key character varying, + ssh_private_key character varying, + created_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL +); + + +-- +-- Name: terraform_aws_using_ami_backends_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.terraform_aws_using_ami_backends_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: terraform_aws_using_ami_backends_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.terraform_aws_using_ami_backends_id_seq OWNED BY public.terraform_aws_using_ami_backends.id; + + -- -- Name: terraform_libvirt_backends; Type: TABLE; Schema: public; Owner: - -- @@ -594,6 +633,13 @@ ALTER TABLE ONLY public.que_jobs ALTER COLUMN id SET DEFAULT nextval('public.que ALTER TABLE ONLY public.terraform_aws_backends ALTER COLUMN id SET DEFAULT nextval('public.terraform_aws_backends_id_seq'::regclass); +-- +-- Name: terraform_aws_using_ami_backends id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.terraform_aws_using_ami_backends ALTER COLUMN id SET DEFAULT nextval('public.terraform_aws_using_ami_backends_id_seq'::regclass); + + -- -- Name: terraform_libvirt_backends id; Type: DEFAULT; Schema: public; Owner: - -- @@ -695,6 +741,14 @@ ALTER TABLE ONLY public.terraform_aws_backends ADD CONSTRAINT terraform_aws_backends_pkey PRIMARY KEY (id); +-- +-- Name: terraform_aws_using_ami_backends terraform_aws_using_ami_backends_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.terraform_aws_using_ami_backends + ADD CONSTRAINT terraform_aws_using_ami_backends_pkey PRIMARY KEY (id); + + -- -- Name: terraform_libvirt_backends terraform_libvirt_backends_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -879,6 +933,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20180621065525'), ('20180621220505'), ('20180623083735'), -('20190106112141'); +('20190106112141'), +('20190519091614'); diff --git a/guix.scm b/guix.scm index d7c7e96..c545a49 100644 --- a/guix.scm +++ b/guix.scm @@ -17,6 +17,7 @@ (build-system ruby-build-system) (inputs `(("ruby-rails" ,ruby-rails) + ("ruby-sass-rails" ,ruby-sass-rails) ("ruby-pg" ,ruby-pg) ("ruby-gds-sso" ,ruby-gds-sso) ("ruby-govuk-admin-template" ,ruby-govuk-admin-template) @@ -67,8 +68,9 @@ `(("ruby-rubocop" ,ruby-rubocop) ("python" ,python) ("ruby-mocha" ,ruby-mocha) - ("coala" ,coala) - ("coala-bears" ,coala-bears))) + ;;("coala" ,coala) + ;;("coala-bears" ,coala-bears) + )) (synopsis "") (description "") (home-page "") @@ -81,7 +83,7 @@ `(,@(package-inputs govuk-mini-environment-admin) ("ruby" ,ruby) ("ruby-rerun" ,ruby-rerun) - ("ruby-annotate" ,ruby-annotate) + ;;("ruby-annotate" ,ruby-annotate) ;; Useful for debugging and required for using Robe (Emacs) ("ruby-byebug" ,ruby-byebug) ("ruby-web-console", ruby-web-console) diff --git a/lib/guix.rb b/lib/guix.rb index a9cc7b0..a228ce4 100644 --- a/lib/guix.rb +++ b/lib/guix.rb @@ -22,4 +22,8 @@ module Guix def self.available_locally? File.exist?('/var/guix/daemon-socket/socket') end + + def self.valid_store_path?(path) + path.starts_with?('/gnu/store/') && !path.match(/\s/) + end end diff --git a/terraform/aws/backend/main.tf b/terraform/aws/backend/main.tf index f0ecb1a..5351a10 100644 --- a/terraform/aws/backend/main.tf +++ b/terraform/aws/backend/main.tf @@ -327,6 +327,7 @@ EOF # This is needed for things like guix copy to work < temp && mv temp .bashrc EOF ] diff --git a/terraform/aws/deploy/main.tf b/terraform/aws/deploy/main.tf new file mode 100644 index 0000000..6c39f9c --- /dev/null +++ b/terraform/aws/deploy/main.tf @@ -0,0 +1,7 @@ + + +# RDS instance running PostgreSQL + +# GuixSD server running the GOV.UK Mini Environment Admin + + diff --git a/terraform/aws_using_ami/backend/guix-daemon.service.tpl b/terraform/aws_using_ami/backend/guix-daemon.service.tpl new file mode 100644 index 0000000..c2e8310 --- /dev/null +++ b/terraform/aws_using_ami/backend/guix-daemon.service.tpl @@ -0,0 +1,17 @@ +[Unit] +Description=Build daemon for GNU Guix + +[Service] +ExecStart=/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon --build-users-group=guixbuild --substitute-urls="${substitute_servers}" --listen=0.0.0.0 --listen=/var/guix/daemon-socket/socket --max-jobs=2 +Environment=GUIX_LOCPATH='/var/guix/profiles/per-user-root/guix-profile/lib/locale' LC_ALL=en_US.utf8 +RemainAfterExit=yes +StandardOutput=syslog +StandardError=syslog + +# See . +# Some package builds (for example, go@1.8.1) may require even more than +# 1024 tasks. +TasksMax=8192 + +[Install] +WantedBy=multi-user.target diff --git a/terraform/aws_using_ami/backend/main.tf b/terraform/aws_using_ami/backend/main.tf new file mode 100644 index 0000000..8dfb078 --- /dev/null +++ b/terraform/aws_using_ami/backend/main.tf @@ -0,0 +1,340 @@ +terraform { + backend "http" {} +} + +variable "aws_access_key" { + type = "string" +} + +variable "aws_secret_key" { + type = "string" +} + +variable "aws_region" { + type = "string" +} + +variable "aws_vpc_id" { + type = "string" +} + +variable "aws_route_53_zone_id" { + type = "string" +} + +variable "ssh_public_key" { + type = "string" +} + +variable "ssh_private_key" { + type = "string" +} + +variable "guix_substitute_servers" { + type = "map" + default = { + "https://ci.guix.gnu.org" = < temp && mv temp .bashrc +EOF + ] + + connection { + type = "ssh" + user = "ubuntu" + private_key = "${var.ssh_private_key}" + } + } +} + +resource "aws_route53_record" "main" { + zone_id = "${data.aws_route53_zone.main.zone_id}" + name = "builder.${var.backend_slug}" + type = "A" + ttl = "60" + records = ["${aws_spot_instance_request.main.public_ip}"] + + depends_on = ["aws_spot_instance_request.main"] +} + +# Outputs + +output "backend_up" { + value = "${length(aws_spot_instance_request.main.public_ip) != 0}" +} + +output "deployer_key_pair_name" { + value = "${aws_key_pair.deployer.key_name}" +} + +output "guix_client_security_group_name" { + value = "${aws_security_group.guix_client.name}" +} + +output "public_webserver_security_group_name" { + value = "${aws_security_group.public_webserver.name}" +} + +output "ssh_access_from_mini_environment_admin_security_group_name" { + value = "${aws_security_group.ssh_access_from_mini_environment_admin.name}" +} + +output "guix_daemon_private_dns" { + value = "${aws_spot_instance_request.main.private_dns}" +} + +output "guix_daemon_public_dns" { + value = "${aws_spot_instance_request.main.public_dns}" +} diff --git a/terraform/aws_using_ami/deploy/main.tf b/terraform/aws_using_ami/deploy/main.tf new file mode 100644 index 0000000..6c39f9c --- /dev/null +++ b/terraform/aws_using_ami/deploy/main.tf @@ -0,0 +1,7 @@ + + +# RDS instance running PostgreSQL + +# GuixSD server running the GOV.UK Mini Environment Admin + + diff --git a/terraform/aws_using_ami/mini_environment/govuk.service.tpl b/terraform/aws_using_ami/mini_environment/govuk.service.tpl new file mode 100644 index 0000000..5c65267 --- /dev/null +++ b/terraform/aws_using_ami/mini_environment/govuk.service.tpl @@ -0,0 +1,12 @@ +[Unit] +Description=GOV.UK +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/home/ubuntu +ExecStart=${start_command} + +[Install] +WantedBy=multi-user.target diff --git a/terraform/aws_using_ami/mini_environment/main.tf b/terraform/aws_using_ami/mini_environment/main.tf new file mode 100644 index 0000000..40a8148 --- /dev/null +++ b/terraform/aws_using_ami/mini_environment/main.tf @@ -0,0 +1,164 @@ +terraform { + backend "http" {} +} + +variable "slug" { + type = "string" +} + +variable "aws_access_key" { + type = "string" +} + +variable "aws_secret_key" { + type = "string" +} + +variable "aws_region" { + type = "string" +} + +variable "aws_route_53_zone_id" { + type = "string" +} + +variable "start_command" { + type = "string" +} + +variable "backend_remote_state_address" { + type = "string" +} + +variable "backend_remote_state_username" { + type = "string" +} + +variable "backend_remote_state_password" { + type = "string" +} + +variable "ssh_private_key" { + type = "string" +} + +provider "aws" { + access_key = "${var.aws_access_key}" + secret_key = "${var.aws_secret_key}" + region = "${var.aws_region}" +} + + +data "terraform_remote_state" "backend" { + backend = "http" + config { + address = "${var.backend_remote_state_address}" + username = "${var.backend_remote_state_username}" + password = "${var.backend_remote_state_password}" + } +} + +data "aws_route53_zone" "main" { + zone_id = "${var.aws_route_53_zone_id}" +} + +data "template_file" "govuk_service" { + template = "${file("${path.module}/govuk.service.tpl")}" + + vars { + start_command = "${var.start_command}" + } +} + + +resource "aws_spot_instance_request" "main" { + ami = "ami-8fd760f6" + instance_type = "t2.xlarge" + key_name = "${data.terraform_remote_state.backend.deployer_key_pair_name}" + security_groups = [ + "${data.terraform_remote_state.backend.guix_client_security_group_name}", + "${data.terraform_remote_state.backend.public_webserver_security_group_name}", + "${data.terraform_remote_state.backend.ssh_access_from_mini_environment_admin_security_group_name}" + ] + + wait_for_fulfillment = true + spot_price = "0.21" + spot_type = "one-time" + + root_block_device { + volume_size = "150" + } + + provisioner "file" { + content = "${data.template_file.govuk_service.rendered}" + destination = "/home/ubuntu/govuk.service" + + connection { + type = "ssh" + user = "ubuntu" + private_key = "${var.ssh_private_key}" + } + } + + provisioner "remote-exec" { + inline = [ + "sudo apt-get update", + "sudo apt-get update", + "sudo apt-get -y install nfs-common cachefilesd", + "sudo tune2fs -o user_xattr /dev/xvda1", + "sudo sed 's/#RUN/RUN/' -i /etc/default/cachefilesd", + "sudo mkdir -p /gnu/store", + "sudo mount -t nfs4 -o ro,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,actimeo=600,fsc,nocto,retrans=2 ${data.terraform_remote_state.backend.efs_file_system_dns_name}:gnu/store /gnu/store", + "sudo mkdir -p /var/guix", + "sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${data.terraform_remote_state.backend.efs_file_system_dns_name}:var/guix /var/guix", + "echo \"export GUIX_DAEMON_SOCKET=guix://${data.terraform_remote_state.backend.guix_daemon_private_dns}\" | sudo tee /etc/profile.d/guix-daemon-socket.sh", + #"sudo systemctl restart cachefilesd", + "sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080", + "sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443", + "sudo iptables -A OUTPUT -t nat -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080", + "sudo iptables -A OUTPUT -t nat -o lo -p tcp --dport 443 -j REDIRECT --to-port 8443", + "sudo mkdir -p /var/cache/govuk-mini-environment-admin /var/lib/govuk-mini-environment-admin /var/log/govuk-mini-environment-admin", + "sudo mv /home/ubuntu/govuk.service /etc/systemd/system/govuk.service", + "sudo systemctl daemon-reload", + "sudo systemctl enable govuk.service", + "sudo systemctl start govuk.service" + ] + + connection { + type = "ssh" + user = "ubuntu" + private_key = "${var.ssh_private_key}" + } + } +} + +resource "aws_route53_record" "main" { + zone_id = "${data.aws_route53_zone.main.zone_id}" + name = "${var.slug}" + type = "A" + ttl = "60" + records = ["${aws_spot_instance_request.main.public_ip}"] +} + +resource "aws_route53_record" "wildcard" { + zone_id = "${data.aws_route53_zone.main.zone_id}" + name = "*.${var.slug}" + type = "A" + ttl = "60" + records = ["${aws_spot_instance_request.main.public_ip}"] +} + +# Outputs + +output "spot_bid_status" { + value = "${aws_spot_instance_request.main.spot_bid_status}" +} + +output "spot_request_status" { + value = "${aws_spot_instance_request.main.spot_request_state}" +} + +output "mini_environment_up" { + value = "${aws_spot_instance_request.main.spot_bid_status == "fulfilled"}" +} + -- cgit v1.2.3