aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Baines <mail@cbaines.net>2019-05-24 16:03:40 +0100
committerChristopher Baines <mail@cbaines.net>2019-05-24 16:03:40 +0100
commit2b784c267f8688111ed3f745454e349500f426bc (patch)
tree71da95b6d23a4c62e6e12953d8f9af8a45f7481a
parentf8fabe092138d4513a139b9f9a0f4aaccacd81b0 (diff)
downloadgovuk-mini-environment-admin-ami-backend.tar
govuk-mini-environment-admin-ami-backend.tar.gz
-rw-r--r--app/controllers/backends/terraform_aws_controller.rb8
-rw-r--r--app/controllers/backends/terraform_aws_using_ami_controller.rb122
-rw-r--r--app/controllers/backends/terraform_libvirt_controller.rb8
-rw-r--r--app/controllers/govuk_guix/revisions_controller.rb5
-rw-r--r--app/jobs/backends/terraform_aws_using_ami_job.rb46
-rw-r--r--app/jobs/govuk_guix/fetch_revision_job.rb4
-rw-r--r--app/models/backends.rb1
-rw-r--r--app/models/backends/terraform_aws.rb2
-rw-r--r--app/models/backends/terraform_aws_using_ami.rb90
-rw-r--r--app/models/backends/terraform_aws_using_ami/backend_methods.rb146
-rw-r--r--app/models/backends/terraform_aws_using_ami/mini_environment_methods.rb113
-rw-r--r--app/services/govuk_guix/build_mini_environment.rb9
-rw-r--r--app/services/govuk_guix/update_gcroots_directory.rb15
-rw-r--r--app/views/backends/terraform_aws_using_ami/in_use_store_paths.html.erb40
-rw-r--r--app/views/backends/terraform_aws_using_ami/new.html.erb192
-rw-r--r--app/views/backends/terraform_aws_using_ami/show.html.erb362
-rw-r--r--app/views/layouts/unauthenticated.html.erb31
-rw-r--r--app/views/mini_environments/unauthenticated_index.html.erb33
-rw-r--r--config/routes.rb10
-rw-r--r--db/migrate/20190519091614_terraform_aws_using_ami.rb17
-rw-r--r--db/structure.sql57
-rw-r--r--guix.scm8
-rw-r--r--lib/guix.rb4
-rw-r--r--terraform/aws/backend/main.tf1
-rw-r--r--terraform/aws/deploy/main.tf7
-rw-r--r--terraform/aws_using_ami/backend/guix-daemon.service.tpl17
-rw-r--r--terraform/aws_using_ami/backend/main.tf340
-rw-r--r--terraform/aws_using_ami/deploy/main.tf7
-rw-r--r--terraform/aws_using_ami/mini_environment/govuk.service.tpl12
-rw-r--r--terraform/aws_using_ami/mini_environment/main.tf164
30 files changed, 1854 insertions, 17 deletions
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 <mail@cbaines.net>
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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 <mail@cbaines.net>
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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 <mail@cbaines.net>
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+
+# == 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 <mail@cbaines.net>
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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 <mail@cbaines.net>
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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 <mail@cbaines.net>
+
+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
+<http://www.gnu.org/licenses/>.
+
+%>
+
+<h1>Backend: <%= @backend.label %></h1>
+<% status = @backend.status %>
+
+<br>
+
+<h3>In use store paths</h3>
+
+<table class="table table-striped">
+ <% @backend.in_use_store_paths.each do |store_path| %>
+ <tr>
+ <td><%= store_path %></td>
+ <td>
+ </td>
+ </tr>
+ <% end %>
+</table>
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 <mail@cbaines.net>
+
+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
+<http://www.gnu.org/licenses/>.
+
+%>
+
+<a href="<%= setup_path %>" class="btn btn-lg btn-default pull-right">
+ Back to setup
+</a>
+
+<h1>Create a new AWS backend</h1>
+
+<div class="row">
+ <div class="col-md-10">
+ <%= form_with(model: @backend,
+ url: { action: "create" },
+ html: { class: "form-horizontal" }) do |f|
+ %>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :label, class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :label,
+ class: 'form-control',
+ placeholder: 'Label for this backend'
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :domain, class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :domain,
+ class: 'form-control',
+ placeholder: 'Domain within which to host mini environments'
+ ) %>
+ <span class="help-block">
+ <p>
+ For example, if you entered <samp>example.com</samp> 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
+ <samp>test.example.com</samp>.
+ </p>
+ <p>
+ A Route53 Hosted Zone will be created for this domain,
+ and records added for the mini environments.
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :aws_region, 'AWS Region', class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :aws_region,
+ class: 'form-control',
+ placeholder: 'What region to use'
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :vpc_id,
+ 'VPC ID',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :vpc_id,
+ class: 'form-control',
+ placeholder: 'The ID of the VPC (Virtual Private Cloud) to use'
+ ) %>
+ <span class="help-block">
+ <p>
+ This VPC (Virtual Private Cloud) should be the one to
+ use for all resources.
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :route_53_zone_id,
+ 'Route 53 Zone ID',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :route_53_zone_id,
+ class: 'form-control',
+ placeholder: 'The ID of the Route 53 Zone to use'
+ ) %>
+ <span class="help-block">
+ <p>
+ This zone should be authoritive for the domain this
+ backend is using. Entries in this zone will be created
+ for the mini environments.
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :aws_access_key_id, 'AWS Access Key ID', class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :aws_access_key_id,
+ class: 'form-control',
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :aws_secret_access_key, 'AWS Secret Access Key', class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.password_field(
+ :aws_secret_access_key,
+ class: 'form-control',
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :ssh_public_key,
+ 'SSH Key, public part',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_area(
+ :ssh_public_key,
+ class: 'form-control',
+ placeholder: 'The public part of the SSH key to use'
+ ) %>
+ <span class="help-block">
+ <p>
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :ssh_private_key,
+ 'SSH Key, private part',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_area(
+ :ssh_private_key,
+ class: 'form-control',
+ placeholder: 'The private part of the SSH key to use'
+ ) %>
+ <span class="help-block">
+ <p>
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <div class="col-sm-offset-4 col-sm-8">
+ <%= f.submit "Create", class: 'btn btn-lg btn-success' %>
+ </div>
+ </div>
+ <% end %>
+ </div>
+</div>
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 <mail@cbaines.net>
+
+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
+<http://www.gnu.org/licenses/>.
+
+%>
+
+<a href="<%= setup_path %>" class="btn btn-lg btn-default pull-right">
+ Back to Setup
+</a>
+
+<h1>Backend: <%= @backend.label %></h1>
+<% status = @backend.status %>
+
+<br>
+
+<div class="row">
+ <div class="col-md-3">
+
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ Current Status<br>
+ <small>updated at <%= status[:updated_at] %></small>
+ </div>
+ <div class="panel-body">
+ <% if status[:running] %>
+ <div class="alert alert-success text-center"
+ role="alert"
+ style="margin-bottom: 0px;">
+ Backend available
+ </div>
+ <% else %>
+ <div class="alert alert-warning text-center"
+ role="alert"
+ style="margin-bottom: 0px;">
+ Backend down
+ </div>
+ <% end %>
+
+ <br>
+ <a href="<%= terraform_http_backend_show_history_path(
+ @backend.terraform_state_id
+ ) %>">
+ View Terraform state information
+ </a>
+ </div>
+ </div>
+
+ <%= form_with(
+ url: perform_action_terraform_aws_using_ami_backend_path(@backend),
+ local: true,
+ method: "post"
+ ) do %>
+
+ <div class="panel panel-default">
+ <div class="panel-heading">Actions</div>
+
+ <ul class="list-group">
+ <li class="list-group-item">
+ <%= submit_tag('Deploy',
+ role: 'button',
+ style: 'margin-bottom: 5px;',
+ class: 'btn btn-lg btn-success btn-block')
+ %>
+ <p class="list-group-item-text">
+ Run Terraform to deploy this backend, ensuring everything
+ is setup to create new mini environments.
+ </p>
+ </li>
+ <li class="list-group-item">
+ <%= submit_tag("Stop",
+ role: 'button',
+ style: 'margin-bottom: 5px;',
+ class: 'btn btn-lg btn-warning btn-block')
+ %>
+ <p class="list-group-item-text">
+ 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.
+ </p>
+ </li>
+ <li class="list-group-item">
+ <%= submit_tag("Destroy",
+ role: 'button',
+ style: 'margin-bottom: 5px;',
+ class: 'btn btn-lg btn-danger btn-block')
+ %>
+ <p class="list-group-item-text">
+ Run Terraform to destroy this backend, note that this will
+ remove all cached data and disrupt all mini environments
+ using this backend.
+ </p>
+ </li>
+ <li class="list-group-item">
+ <%= submit_tag("Refresh state",
+ role: 'button',
+ style: 'margin-bottom: 5px;',
+ class: 'btn btn-lg btn-info btn-block')
+ %>
+ <p class="list-group-item-text">
+ Run Terraform to refresh the state information for this
+ backend.
+ </p>
+ </li>
+ </ul>
+ </div>
+ <% end %>
+ </div>
+ <div class="col-md-9">
+ <%= render(
+ partial: 'shared/jobs',
+ locals: {
+ jobs: Backends::TerraformAwsJob.jobs(@backend.id).order(id: :desc)
+ }
+ ) %>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col-md-8">
+ <h3>Update details</h3>
+ <br>
+
+ <%= form_with(model: @backend,
+ url: { action: "update" },
+ html: { class: "form-horizontal" }) do |f|
+ %>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :label, class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :label,
+ class: 'form-control',
+ placeholder: 'Label for this backend'
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :domain, class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :domain,
+ class: 'form-control',
+ placeholder: 'Domain within which to host mini environments',
+ readonly: true
+ ) %>
+ <span class="help-block">
+ <p>
+ For example, if you entered <samp>example.com</samp> 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
+ <samp>test.example.com</samp>.
+ </p>
+ <p>
+ A Route53 Hosted Zone will be created for this domain,
+ and records added for the mini environments.
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :aws_region, 'AWS Region', class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :aws_region,
+ class: 'form-control',
+ placeholder: 'What region to use',
+ readonly: true,
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :vpc_id,
+ 'VPC ID',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :vpc_id,
+ class: 'form-control',
+ placeholder: 'The ID of the VPC (Virtual Private Cloud) to use',
+ readonly: true
+ ) %>
+ <span class="help-block">
+ <p>
+ This VPC (Virtual Private Cloud) should be the one to
+ use for all resources.
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :route_53_zone_id,
+ 'Route 53 Zone ID',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :route_53_zone_id,
+ class: 'form-control',
+ placeholder: 'The ID of the Route 53 Zone to use',
+ readonly: true
+ ) %>
+ <span class="help-block">
+ <p>
+ This zone should be authoritive for the domain this
+ backend is using. Entries in this zone will be created
+ for the mini environments.
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :aws_access_key_id, 'AWS Access Key ID', class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.text_field(
+ :aws_access_key_id,
+ class: 'form-control',
+ ) %>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label :aws_secret_access_key, 'AWS Secret Access Key', class: 'col-sm-4 control-label' %>
+ <div class="col-sm-8">
+ <%= f.password_field(
+ :aws_secret_access_key,
+ class: 'form-control',
+ placeholder: 'Secret key hidden',
+ ) %>
+ <span id="helpBlock" class="help-block">
+ The AWS Secret Access Key is not accessible once entered.
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :ssh_public_key,
+ 'SSH Key, public part',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= f.text_area(
+ :ssh_public_key,
+ class: 'form-control',
+ placeholder: 'The public part of the SSH key to use',
+ readonly: true
+ ) %>
+ <span class="help-block">
+ <p>
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <%= f.label(
+ :ssh_private_key,
+ 'SSH Key, private part',
+ class: 'col-sm-4 control-label'
+ ) %>
+ <div class="col-sm-8">
+ <%= text_area_tag(
+ :ssh_private_key,
+ 'Secret key hidden',
+ class: 'form-control',
+ disabled: true
+ ) %>
+ <span class="help-block">
+ <p>
+ </p>
+ </span>
+ </div>
+ </div>
+
+ <div class="form-group form-group-lg">
+ <div class="col-sm-offset-2 col-sm-10">
+ <%= f.submit "Save", class: 'btn btn-lg btn-success' %>
+ </div>
+ </div>
+ <% end %>
+ </div>
+ <div class="col-md-4">
+ <h3>Delete backend</h3>
+ <br>
+
+ <% unless @backend.mini_environments.empty? %>
+ <p>
+ Unable to delete backend, as mini environments using this
+ backend still exist.
+ </p>
+
+ <p>
+ To delete this backend, first delete all the mini environments
+ using it.
+ </p>
+ <% 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 %>
+
+ </div>
+</div>
+
+<h3>Mini environments</h3>
+
+<table class="table table-striped">
+ <tr>
+ <th>Name</th>
+ <th></th>
+ </tr>
+ <% @backend.mini_environments.each do |mini_environment| %>
+ <tr>
+ <td><%= mini_environment.name %></td>
+ <td>
+ <a class="btn btn-default btn-lg pull-right"
+ role="button"
+ href="<%= mini_environment_path(mini_environment) %>">
+ Show details
+ </a>
+ </td>
+ </tr>
+ <% end %>
+</table>
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 <mail@cbaines.net>
+
+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
+<http://www.gnu.org/licenses/>.
+
+%>
+
+<% 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 <mail@cbaines.net>
+
+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
+<http://www.gnu.org/licenses/>.
+
+%>
+
+<div class="jumbotron">
+ <h1>Quickly and easily create standalone GOV.UK environments</h1>
+ <p>...</p>
+ <p>
+ <a class="btn btn-primary btn-lg" href="/auth/gds" role="button">
+ Sign in
+ </a>
+ </p>
+</div>
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
@@ -459,6 +459,45 @@ ALTER SEQUENCE public.terraform_aws_backends_id_seq OWNED BY public.terraform_aw
--
+-- 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: -
--
@@ -595,6 +634,13 @@ ALTER TABLE ONLY public.terraform_aws_backends ALTER COLUMN id SET DEFAULT nextv
--
+-- 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: -
--
@@ -696,6 +742,14 @@ ALTER TABLE ONLY public.terraform_aws_backends
--
+-- 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
<<EOF
echo 'GUIX_PROFILE=/home/ubuntu/.guix-profile; source /home/ubuntu/.guix-profile/etc/profile
+GUIX_PROFILE=/home/ubuntu/.config/guix/current; source /home/ubuntu/.config/guix/current/etc/profile
' | cat - .bashrc > 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 <https://lists.gnu.org/archive/html/guix-devel/2016-04/msg00608.html>.
+# 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" = <<EOF
+(entry
+ (public-key
+ (ecc
+ (curve Ed25519)
+ (q #8D156F295D24B0D9A86FA5741A840FF2D24F60F7B6C4134814AD55625971B394#)
+ )
+ )
+ (tag
+ (guix import)
+ )
+)
+EOF
+ }
+}
+
+variable "mini_environment_admin_guix_public_key" {
+ type = "string"
+}
+
+variable "mini_environment_admin_egress_cidr_blocks" {
+ type = "list"
+}
+
+variable "backend_slug" {
+ type = "string"
+}
+
+locals {
+ guix_daemon_substitute_servers = "${join(" ", keys(var.guix_substitute_servers))}"
+}
+
+provider "aws" {
+ access_key = "${var.aws_access_key}"
+ secret_key = "${var.aws_secret_key}"
+ region = "${var.aws_region}"
+}
+
+data "aws_route53_zone" "main" {
+ zone_id = "${var.aws_route_53_zone_id}"
+}
+
+data "template_file" "guix_daemon_service" {
+ template = "${file("${path.module}/guix-daemon.service.tpl")}"
+
+ vars {
+ substitute_servers = "${local.guix_daemon_substitute_servers}"
+ }
+}
+
+data "aws_availability_zones" "available" {}
+
+resource "aws_key_pair" "deployer" {
+ key_name = "govuk_mini_environment_admin/${var.backend_slug}/deployer"
+ public_key = "${var.ssh_public_key}"
+}
+
+resource "aws_security_group" "public_webserver" {
+ name = "govuk_mini_environment_admin/${var.backend_slug}/public_webserver"
+ description = "For instances running public facing web servers"
+ vpc_id = "${var.aws_vpc_id}"
+
+ ingress {
+ from_port = 0
+ to_port = 80
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+
+ ingress {
+ from_port = 0
+ to_port = 443
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+
+ ingress {
+ from_port = 0
+ to_port = 8080
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+
+ ingress {
+ from_port = 0
+ to_port = 8443
+ protocol = "tcp"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+}
+
+resource "aws_security_group" "ssh_access_from_mini_environment_admin" {
+ name = "govuk_mini_environment_admin/${var.backend_slug}/ssh_access_from_mini_environment_admin"
+ description = "For instances that need SSH access for Terraform and Guix builds"
+ vpc_id = "${var.aws_vpc_id}"
+
+ ingress {
+ from_port = 0
+ to_port = 22
+ protocol = "tcp"
+ cidr_blocks = [
+ "${var.mini_environment_admin_egress_cidr_blocks}"
+ ]
+ }
+}
+
+resource "aws_security_group" "guix_client" {
+ name = "govuk_mini_environment_admin/${var.backend_slug}/guix_client"
+ description = "For instances with access to the guix_daemon instance"
+ vpc_id = "${var.aws_vpc_id}"
+
+ egress {
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+}
+
+resource "aws_security_group" "guix_daemon" {
+ name = "govuk_mini_environment_admin/${var.backend_slug}/guix_daemon"
+ description = "For the guix_daemon instance."
+ vpc_id = "${var.aws_vpc_id}"
+
+ ingress {
+ from_port = 44146
+ to_port = 44146
+ protocol = "tcp"
+ security_groups = ["${aws_security_group.guix_client.id}"]
+ }
+
+ egress {
+ from_port = 0
+ to_port = 0
+ protocol = "-1"
+ cidr_blocks = ["0.0.0.0/0"]
+ }
+}
+
+resource "aws_spot_instance_request" "main" {
+ ami = "ami-0b2a4d260c54e8d3d"
+ instance_type = "i3.xlarge"
+ key_name = "${aws_key_pair.deployer.key_name}"
+ security_groups = [
+ "${aws_security_group.guix_daemon.name}",
+ "${aws_security_group.ssh_access_from_mini_environment_admin.name}"
+ ]
+
+ wait_for_fulfillment = true
+ spot_price = "0.172"
+ spot_type = "one-time"
+ availability_zone = "${data.aws_availability_zones.available.names[1]}"
+
+ root_block_device {
+ volume_size = "50"
+ }
+
+ provisioner "file" {
+ content = "${data.template_file.guix_daemon_service.rendered}"
+ destination = "/home/ubuntu/guix-daemon.service"
+
+ connection {
+ type = "ssh"
+ user = "ubuntu"
+ private_key = "${var.ssh_private_key}"
+ }
+ }
+
+ provisioner "file" {
+ content = "(acl\n ${join("\n", values(var.guix_substitute_servers))}\n${var.mini_environment_admin_guix_public_key}\n)\n"
+ destination = "/home/ubuntu/acl"
+
+ connection {
+ type = "ssh"
+ user = "ubuntu"
+ private_key = "${var.ssh_private_key}"
+ }
+ }
+
+ provisioner "remote-exec" {
+ inline = [
+ <<EOF
+sudo sgdisk --largest-new=0 /dev/nvme0n1
+
+sudo mkfs.ext4 -m 0 /dev/nvme0n1p1
+
+echo "/dev/nvme0n1p1 /gnu/store ext4 defaults,discard 0 0" | sudo tee -a /etc/fstab
+
+sudo cat /etc/fstab
+
+sudo mkdir -p /gnu/store
+sudo mount -a
+EOF
+ ,
+ <<EOF
+set -e
+
+FILENAME="guix-binary-1.0.0.x86_64-linux.tar.xz"
+
+wget https://ftp.gnu.org/gnu/guix/$FILENAME
+
+cd /
+sudo tar --warning=no-timestamp -xf /home/ubuntu/$FILENAME
+cd -
+
+rm "$FILENAME"
+
+sudo mkdir -p ~root/.config/guix
+sudo ln -sf /var/guix/profiles/per-user/root/current-guix ~root/.config/guix/current
+
+sudo mkdir -p /usr/local/bin
+sudo ln --symbolic --no-target-directory /var/guix/profiles/per-user/root/current-guix/bin/guix /usr/local/bin/guix
+EOF
+ ,
+ "sudo mv /home/ubuntu/guix-daemon.service /etc/systemd/system/guix-daemon.service",
+ "sudo mkdir /etc/guix",
+ "sudo mv /home/ubuntu/acl /etc/guix/acl",
+ "sudo groupadd --system guixbuild",
+ <<EOF
+for i in `seq -w 1 32`;
+do
+ sudo useradd -g guixbuild -G guixbuild \
+ -d /var/empty -s `which nologin` \
+ -c "Guix build user $i" --system \
+ guix$i;
+done
+EOF
+ ,
+ # Disable apparmor, as it seems to cause guile to segfault
+ # sometimes
+ "sudo systemctl stop apparmor",
+ "sudo service apparmor teardown",
+ "sudo systemctl disable apparmor",
+ "sudo systemctl daemon-reload",
+ "sudo systemctl enable guix-daemon.service",
+ "sudo systemctl start guix-daemon.service",
+ "sudo mkdir -p /var/guix/gcroots/govuk-development-data /var/guix/gcroots/govuk-mini-environment-admin",
+ "sudo chown ubuntu:ubuntu /var/guix/gcroots/govuk-development-data /var/guix/gcroots/govuk-mini-environment-admin",
+ "mkdir -p ~/.config", # Make the ~/.config directory, to ensure it's owned by ubuntu
+ # Setup the profile for the ubuntu user
+ <<EOF
+if [ ! -d "/var/guix/profiles/per-user/ubuntu" ]; then
+ # Install openssl and nss-certs so that downloads work for custom
+ # package versions, and guile-sqlite3
+ /var/guix/profiles/per-user/root/current-guix/bin/guix package --fallback -i glibc-locales guile bash openssl nss-certs guile-sqlite3
+fi
+EOF
+ ,
+ # This is needed for things like guix copy to work
+ <<EOF
+echo 'export GUIX_LOCPATH="$HOME/.guix-profile/lib/locale"
+
+GUIX_PROFILE=/home/ubuntu/.guix-profile; source /home/ubuntu/.guix-profile/etc/profile
+GUIX_PROFILE=/home/ubuntu/.config/guix/current; source /home/ubuntu/.config/guix/current/etc/profile
+' | cat - .bashrc > 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"}"
+}
+