# frozen_string_literal: true require_dependency 'api/validations/validators/limit' module API module Terraform class State < Grape::API include ::Gitlab::Utils::StrongMemoize default_format :json before do authenticate! authorize! :admin_terraform_state, user_project end params do requires :id, type: String, desc: 'The ID of a project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do namespace ':id/terraform/state/:name' do params do requires :name, type: String, desc: 'The name of a Terraform state' optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID' end helpers do def remote_state_handler ::Terraform::RemoteStateHandler.new(user_project, current_user, name: params[:name], lock_id: params[:ID]) end end desc 'Get a terraform state by its name' route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth get do remote_state_handler.find_with_lock do |state| no_content! unless state.file.exists? env['api.format'] = :binary # this bypasses json serialization body state.file.read status :ok end end desc 'Add a new terraform state or update an existing one' route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth post do data = request.body.read no_content! if data.empty? remote_state_handler.handle_with_lock do |state| state.file = CarrierWaveStringFile.new(data) state.save! status :ok end end desc 'Delete a terraform state of a certain name' route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth delete do remote_state_handler.handle_with_lock do |state| state.destroy! status :ok end end desc 'Lock a terraform state of a certain name' route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth params do requires :ID, type: String, limit: 255, desc: 'Terraform state lock ID' requires :Operation, type: String, desc: 'Terraform operation' requires :Info, type: String, desc: 'Terraform info' requires :Who, type: String, desc: 'Terraform state lock owner' requires :Version, type: String, desc: 'Terraform version' requires :Created, type: String, desc: 'Terraform state lock timestamp' requires :Path, type: String, desc: 'Terraform path' end post '/lock' do status_code = :ok lock_info = { 'Operation' => params[:Operation], 'Info' => params[:Info], 'Version' => params[:Version], 'Path' => params[:Path] } begin remote_state_handler.lock! rescue ::Terraform::RemoteStateHandler::StateLockedError status_code = :conflict end remote_state_handler.find_with_lock do |state| lock_info['ID'] = state.lock_xid lock_info['Who'] = state.locked_by_user.username lock_info['Created'] = state.locked_at env['api.format'] = :binary # this bypasses json serialization body lock_info.to_json status status_code end end desc 'Unlock a terraform state of a certain name' route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth params do optional :ID, type: String, limit: 255, desc: 'Terraform state lock ID' end delete '/lock' do remote_state_handler.unlock! status :ok rescue ::Terraform::RemoteStateHandler::StateLockedError status :conflict end end end end end end