Skip to content

Instantly share code, notes, and snippets.

@janko

janko/shrine-cli.rb

Created Mar 24, 2020
Embed
What would you like to do?
The CLI I wrote for creating new AWS S3 buckets for reproducing issues reported for Shrine. It's can serve as an example for programmatically creating new S3 buckets with bucket-specific credentials.
#!/usr/bin/env ruby
require "dry/cli"
require "aws-sdk-s3"
require "aws-sdk-iam"
require "json"
ENV["AWS_ACCESS_KEY_ID"] = "..."
ENV["AWS_SECRET_ACCESS_KEY"] = "..."
ENV["AWS_REGION"] = "..."
if Warning.respond_to?(:[]=) # Ruby 2.7+
Warning[:deprecated] = false # TODO: remove when dry-cli fixes kwargs warnings
end
module Commands
extend Dry::CLI::Registry
class S3
DEFAULT_NAME = "shrine-testing"
class Command < Dry::CLI::Command
private
def s3
Aws::S3::Client.new
end
def iam
Aws::IAM::Client.new
end
end
class Create < Command
DEFAULT_PERMISSIONS = %w[
GetObject
PutObject
PutObjectAcl
DeleteObject
ListMultipartUploadParts
AbortMultipartUpload
]
argument :name, default: DEFAULT_NAME, desc: "The name of the bucket/user"
option :permissions, type: :array, default: DEFAULT_PERMISSIONS, desc: "list of S3 permissions"
def call(name:, permissions:, **)
create_bucket(name)
create_user(name, permissions)
end
private
def create_bucket(name)
create_s3_bucket(name)
create_s3_bucket_cors(name)
end
def create_user(name, permissions)
create_iam_user(name)
create_iam_policy(name, permissions)
create_iam_access_key(name)
end
def create_s3_bucket(name)
s3.create_bucket(bucket: name)
end
def create_s3_bucket_cors(name)
s3.put_bucket_cors(
bucket: name,
cors_configuration: {
cors_rules: [
{
allowed_origins: %w[*],
allowed_methods: %w[GET POST PUT],
allowed_headers: %w[Authorization x-amz-date x-amz-content-sha256 content-type],
expose_headers: %w[ETag],
max_age_seconds: 3000,
},
{
allowed_origins: %w[*],
allowed_methods: %w[GET],
max_age_seconds: 3000,
},
]
}
)
end
def create_iam_user(name)
iam.create_user(user_name: name)
rescue Aws::IAM::Errors::EntityAlreadyExists
warn "User #{name.inspect} already exists"
end
def create_iam_policy(name, permissions)
iam.put_user_policy(
user_name: name,
policy_name: name,
policy_document: JSON.generate(
"Version" => "2012-10-17",
"Statement" => [
{
"Effect" => "Allow",
"Action" => ["s3:ListBucket"], # for listing objects in a bucket
"Resource" => ["arn:aws:s3:::#{name}/*"],
},
{
"Effect" => "Allow",
"Action" => permissions.map { |name| "s3:#{name}" },
"Resource" => ["arn:aws:s3:::#{name}/*"],
},
]
)
)
end
def create_iam_access_key(name)
# delete any previous access keys
response = iam.list_access_keys(user_name: name)
response.access_key_metadata.each do |access_key|
iam.delete_access_key(user_name: name, access_key_id: access_key.access_key_id)
end
response = iam.create_access_key(user_name: name)
puts "access_key_id: #{response.access_key.access_key_id}"
puts "secret_access_key: #{response.access_key.secret_access_key}"
end
end
class Delete < Command
argument :name, default: DEFAULT_NAME, desc: "The name of the bucket/user"
def call(name:, **)
delete_user(name)
delete_bucket(name)
end
private
def delete_user(name)
delete_iam_policy(name)
delete_iam_access_keys(name)
delete_iam_user(name)
end
def delete_bucket(name)
delete_s3_bucket(name)
end
def delete_iam_policy(name)
iam.delete_user_policy(user_name: name, policy_name: name)
rescue Aws::IAM::Errors::NoSuchEntity
end
def delete_iam_access_keys(name)
response = iam.list_access_keys(user_name: name)
response.access_key_metadata.each do |access_key|
iam.delete_access_key(user_name: name, access_key_id: access_key.access_key_id)
end
rescue Aws::IAM::Errors::NoSuchEntity
end
def delete_iam_user(name)
iam.delete_user(user_name: name)
rescue Aws::IAM::Errors::NoSuchEntity
end
def delete_s3_bucket(name)
s3.delete_bucket(bucket: name)
rescue Aws::S3::Errors::NoSuchBucket
end
end
end
end
Commands.register "s3 create", Commands::S3::Create
Commands.register "s3 delete", Commands::S3::Delete
Dry::CLI.new(Commands).call
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment