diff options
author | Tiger <twatson@gitlab.com> | 2019-08-23 15:38:25 +1000 |
---|---|---|
committer | Tiger <twatson@gitlab.com> | 2019-09-02 13:30:35 +1000 |
commit | d0b403a289a924421633ead6f3ab1dbd4fbec051 (patch) | |
tree | d79c0d024aca8ef44922b53cb6458f1d48a54e9a | |
parent | 34357e03866e73c17df60ba003d46482193cbcd4 (diff) | |
download | gitlab-ce-46686-aws-cluster-provider.tar.gz |
Add AWS cluster provider46686-aws-cluster-provider
Almost identical to the existing GCP cluster data model,
introduce model to represent an EKS cluster that has been
created by GitLab.
-rw-r--r-- | app/models/clusters/cluster.rb | 21 | ||||
-rw-r--r-- | app/models/clusters/concerns/provider_status.rb | 50 | ||||
-rw-r--r-- | app/models/clusters/providers/aws.rb | 53 | ||||
-rw-r--r-- | app/models/clusters/providers/gcp.rb | 51 | ||||
-rw-r--r-- | db/migrate/20190821040941_create_cluster_providers_aws.rb | 32 | ||||
-rw-r--r-- | db/schema.rb | 18 | ||||
-rw-r--r-- | lib/gitlab/regex.rb | 9 | ||||
-rw-r--r-- | lib/gitlab/usage_data.rb | 1 | ||||
-rw-r--r-- | spec/db/schema_spec.rb | 1 | ||||
-rw-r--r-- | spec/factories/clusters/clusters.rb | 8 | ||||
-rw-r--r-- | spec/factories/clusters/providers/aws.rb | 34 | ||||
-rw-r--r-- | spec/lib/gitlab/regex_spec.rb | 9 | ||||
-rw-r--r-- | spec/models/clusters/cluster_spec.rb | 10 | ||||
-rw-r--r-- | spec/models/clusters/providers/aws_spec.rb | 110 | ||||
-rw-r--r-- | spec/models/clusters/providers/gcp_spec.rb | 115 | ||||
-rw-r--r-- | spec/support/shared_examples/models/clusters/providers/provider_status.rb | 67 |
16 files changed, 467 insertions, 122 deletions
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 444e1a82c97..a9392bd40f9 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -34,6 +34,7 @@ module Clusters # we force autosave to happen when we save `Cluster` model has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true + has_one :provider_aws, class_name: 'Clusters::Providers::Aws', autosave: true has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', inverse_of: :cluster, autosave: true @@ -89,14 +90,20 @@ module Clusters enum provider_type: { user: 0, - gcp: 1 + gcp: 1, + aws: 2 } scope :enabled, -> { where(enabled: true) } scope :disabled, -> { where(enabled: false) } - scope :user_provided, -> { where(provider_type: ::Clusters::Cluster.provider_types[:user]) } - scope :gcp_provided, -> { where(provider_type: ::Clusters::Cluster.provider_types[:gcp]) } - scope :gcp_installed, -> { gcp_provided.includes(:provider_gcp).where(cluster_providers_gcp: { status: ::Clusters::Providers::Gcp.state_machines[:status].states[:created].value }) } + + scope :user_provided, -> { where(provider_type: provider_types[:user]) } + scope :gcp_provided, -> { where(provider_type: provider_types[:gcp]) } + scope :aws_provided, -> { where(provider_type: provider_types[:aws]) } + + scope :gcp_installed, -> { gcp_provided.joins(:provider_gcp).merge(Clusters::Providers::Gcp.created) } + scope :aws_installed, -> { aws_provided.joins(:provider_aws).merge(Clusters::Providers::Aws.created) } + scope :managed, -> { where(managed: true) } scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) } @@ -139,7 +146,11 @@ module Clusters end def provider - return provider_gcp if gcp? + if gcp? + provider_gcp + elsif aws? + provider_aws + end end def platform diff --git a/app/models/clusters/concerns/provider_status.rb b/app/models/clusters/concerns/provider_status.rb new file mode 100644 index 00000000000..9141d8fea8a --- /dev/null +++ b/app/models/clusters/concerns/provider_status.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Clusters + module Concerns + module ProviderStatus + extend ActiveSupport::Concern + + included do + scope :created, -> { where(status: state_machines[:status].states[:created].value) } + + state_machine :status, initial: :scheduled do + state :scheduled, value: 1 + state :creating, value: 2 + state :created, value: 3 + state :errored, value: 4 + + event :make_creating do + transition any - [:creating] => :creating + end + + event :make_created do + transition any - [:created] => :created + end + + event :make_errored do + transition any - [:errored] => :errored + end + + before_transition any => [:errored, :created] do |provider| + provider.nullify_credentials + end + + before_transition any => [:creating] do |provider, transition| + operation_id = transition.args.first + provider.assign_operation_id(operation_id) if operation_id + end + + before_transition any => [:errored] do |provider, transition| + status_reason = transition.args.first + provider.status_reason = status_reason if status_reason + end + end + + def on_creation? + scheduled? || creating? + end + end + end + end +end diff --git a/app/models/clusters/providers/aws.rb b/app/models/clusters/providers/aws.rb new file mode 100644 index 00000000000..77d9114093e --- /dev/null +++ b/app/models/clusters/providers/aws.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Clusters + module Providers + class Aws < ApplicationRecord + include Clusters::Concerns::ProviderStatus + + self.table_name = 'cluster_providers_aws' + + belongs_to :cluster, inverse_of: :provider_aws, class_name: 'Clusters::Cluster' + + default_value_for :region, 'us-east-1' + default_value_for :num_nodes, 3 + default_value_for :machine_type, 'm5.large' + + attr_encrypted :secret_access_key, + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-cbc' + + validates :aws_account_id, + length: { is: 12 }, + format: { + with: Gitlab::Regex.aws_account_id_regex, + message: Gitlab::Regex.aws_account_id_regex_message + } + + validates :num_nodes, + presence: true, + numericality: { + only_integer: true, + greater_than: 0 + } + + validates :region, :machine_type, presence: true + + before_validation :sanitize_aws_account_id + + def nullify_credentials + assign_attributes( + access_key_id: nil, + secret_access_key: nil + ) + end + + private + + def sanitize_aws_account_id + self.aws_account_id&.gsub!(/\D/, '') + end + end + end +end diff --git a/app/models/clusters/providers/gcp.rb b/app/models/clusters/providers/gcp.rb index 390748bf252..0069946132d 100644 --- a/app/models/clusters/providers/gcp.rb +++ b/app/models/clusters/providers/gcp.rb @@ -3,6 +3,8 @@ module Clusters module Providers class Gcp < ApplicationRecord + include Clusters::Concerns::ProviderStatus + self.table_name = 'cluster_providers_gcp' belongs_to :cluster, inverse_of: :provider_gcp, class_name: 'Clusters::Cluster' @@ -32,50 +34,23 @@ module Clusters greater_than: 0 } - state_machine :status, initial: :scheduled do - state :scheduled, value: 1 - state :creating, value: 2 - state :created, value: 3 - state :errored, value: 4 - - event :make_creating do - transition any - [:creating] => :creating - end - - event :make_created do - transition any - [:created] => :created - end - - event :make_errored do - transition any - [:errored] => :errored - end - - before_transition any => [:errored, :created] do |provider| - provider.access_token = nil - provider.operation_id = nil - end - - before_transition any => [:creating] do |provider, transition| - operation_id = transition.args.first - raise ArgumentError.new('operation_id is required') unless operation_id.present? - - provider.operation_id = operation_id - end + def api_client + return unless access_token - before_transition any => [:errored] do |provider, transition| - status_reason = transition.args.first - provider.status_reason = status_reason if status_reason - end + @api_client ||= GoogleApi::CloudPlatform::Client.new(access_token, nil) end - def on_creation? - scheduled? || creating? + def nullify_credentials + assign_attributes( + access_token: nil, + operation_id: nil + ) end - def api_client - return unless access_token + def assign_operation_id(operation_id) + raise ArgumentError.new('operation_id is required') unless operation_id.present? - @api_client ||= GoogleApi::CloudPlatform::Client.new(access_token, nil) + assign_attributes(operation_id: operation_id) end end end diff --git a/db/migrate/20190821040941_create_cluster_providers_aws.rb b/db/migrate/20190821040941_create_cluster_providers_aws.rb new file mode 100644 index 00000000000..1dcc32ec09a --- /dev/null +++ b/db/migrate/20190821040941_create_cluster_providers_aws.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class CreateClusterProvidersAws < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :cluster_providers_aws do |t| + t.references :cluster, null: false, type: :bigint, index: { unique: true }, foreign_key: { on_delete: :cascade } + + t.integer :status, null: false + t.text :status_reason + + t.string :aws_account_id, null: false, limit: 12 + t.string :region, null: false, limit: 64 + t.integer :num_nodes, null: false + t.string :machine_type, null: false, limit: 64 + + t.string :access_key_id, limit: 64 + t.text :encrypted_secret_access_key + t.string :encrypted_secret_access_key_iv, limit: 255 + + t.timestamps_with_timezone null: false + + t.index [:cluster_id, :status] + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 5999a859e77..add2d63d909 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -905,6 +905,23 @@ ActiveRecord::Schema.define(version: 2019_08_28_083843) do t.index ["project_id"], name: "index_cluster_projects_on_project_id" end + create_table "cluster_providers_aws", force: :cascade do |t| + t.bigint "cluster_id", null: false + t.integer "status", null: false + t.text "status_reason" + t.string "aws_account_id", null: false + t.string "region", null: false + t.integer "num_nodes", null: false + t.string "machine_type", null: false + t.string "access_key_id" + t.text "encrypted_secret_access_key" + t.string "encrypted_secret_access_key_iv" + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false + t.index ["cluster_id", "status"], name: "index_cluster_providers_aws_on_cluster_id_and_status" + t.index ["cluster_id"], name: "index_cluster_providers_aws_on_cluster_id", unique: true + end + create_table "cluster_providers_gcp", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "status" @@ -3790,6 +3807,7 @@ ActiveRecord::Schema.define(version: 2019_08_28_083843) do add_foreign_key "cluster_platforms_kubernetes", "clusters", on_delete: :cascade add_foreign_key "cluster_projects", "clusters", on_delete: :cascade add_foreign_key "cluster_projects", "projects", on_delete: :cascade + add_foreign_key "cluster_providers_aws", "clusters", on_delete: :cascade add_foreign_key "cluster_providers_gcp", "clusters", on_delete: :cascade add_foreign_key "clusters", "users", on_delete: :nullify add_foreign_key "clusters_applications_cert_managers", "clusters", on_delete: :cascade diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index e6372a42dda..a366b84cfdd 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -115,5 +115,14 @@ module Gitlab def jira_transition_id_regex @jira_transition_id_regex ||= /\d+/ end + + # https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html + def aws_account_id_regex + /\A\d+\z/ + end + + def aws_account_id_regex_message + "can contain only digits, optionally separated by '-'" + end end end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index a93301cb4ce..4338c9afc25 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -66,6 +66,7 @@ module Gitlab clusters_disabled: count(::Clusters::Cluster.disabled), project_clusters_disabled: count(::Clusters::Cluster.disabled.project_type), group_clusters_disabled: count(::Clusters::Cluster.disabled.group_type), + clusters_platforms_eks: count(::Clusters::Cluster.aws_installed.enabled), clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled), clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled), clusters_applications_helm: count(::Clusters::Applications::Helm.available), diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 52af470efac..c0578773ac9 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -24,6 +24,7 @@ describe 'Database schema' do ci_pipelines: %w[user_id], ci_runner_projects: %w[runner_id], ci_trigger_requests: %w[commit_id], + cluster_providers_aws: %w[aws_account_id access_key_id], cluster_providers_gcp: %w[gcp_project_id operation_id], deploy_keys_projects: %w[deploy_key_id], deployments: %w[deployable_id environment_id user_id], diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb index d294e6d055e..39ab9685584 100644 --- a/spec/factories/clusters/clusters.rb +++ b/spec/factories/clusters/clusters.rb @@ -49,6 +49,14 @@ FactoryBot.define do platform_kubernetes factory: [:cluster_platform_kubernetes, :configured] end + trait :provided_by_aws do + provider_type :aws + platform_type :kubernetes + + provider_aws factory: [:cluster_provider_aws, :created] + platform_kubernetes factory: [:cluster_platform_kubernetes, :configured] + end + trait :providing_by_gcp do provider_type :gcp provider_gcp factory: [:cluster_provider_gcp, :creating] diff --git a/spec/factories/clusters/providers/aws.rb b/spec/factories/clusters/providers/aws.rb new file mode 100644 index 00000000000..ff4fae9195a --- /dev/null +++ b/spec/factories/clusters/providers/aws.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :cluster_provider_aws, class: Clusters::Providers::Aws do + cluster + aws_account_id '123456789012' + + trait :scheduled do + access_key_id 'access_key_id' + secret_access_key 'secret_access_key' + end + + trait :creating do + access_key_id 'access_key_id' + secret_access_key 'secret_access_key' + + after(:build) do |provider| + provider.make_creating + end + end + + trait :created do + after(:build) do |provider| + provider.make_created + end + end + + trait :errored do + after(:build) do |provider| + provider.make_errored('An error occurred') + end + end + end +end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index e19210d8fbf..1341602d748 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -65,4 +65,13 @@ describe Gitlab::Regex do it { is_expected.not_to match('.my/image') } it { is_expected.not_to match('my/image.') } end + + describe '.aws_account_id_regex' do + subject { described_class.aws_account_id_regex } + + it { is_expected.to match('12345') } + it { is_expected.to match('123456789012') } + it { is_expected.not_to match('abcdefg') } + it { is_expected.not_to match('1234-5678-9012') } + end end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 9afbe6328ca..7337871224b 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -16,6 +16,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do it { is_expected.to have_many(:cluster_groups) } it { is_expected.to have_many(:groups) } it { is_expected.to have_one(:provider_gcp) } + it { is_expected.to have_one(:provider_aws) } it { is_expected.to have_one(:platform_kubernetes) } it { is_expected.to have_one(:application_helm) } it { is_expected.to have_one(:application_ingress) } @@ -374,7 +375,14 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do it 'returns a provider' do is_expected.to eq(cluster.provider_gcp) - expect(subject.class.name.deconstantize).to eq(Clusters::Providers.to_s) + end + end + + context 'when provider is aws' do + let(:cluster) { create(:cluster, :provided_by_aws) } + + it 'returns a provider' do + is_expected.to eq(cluster.provider_aws) end end diff --git a/spec/models/clusters/providers/aws_spec.rb b/spec/models/clusters/providers/aws_spec.rb new file mode 100644 index 00000000000..1f39539caf8 --- /dev/null +++ b/spec/models/clusters/providers/aws_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Clusters::Providers::Aws do + it { is_expected.to belong_to(:cluster) } + it { is_expected.to validate_presence_of(:region) } + + include_examples 'provider status', :cluster_provider_aws + + describe 'default_value_for' do + let(:provider) { build(:cluster_provider_aws) } + + it "sets default values" do + expect(provider.region).to eq('us-east-1') + expect(provider.num_nodes).to eq(3) + expect(provider.machine_type).to eq('m5.large') + end + end + + describe 'validation' do + subject { provider.valid? } + + context ':aws_account_id' do + let(:provider) { build(:cluster_provider_aws, aws_account_id: aws_account_id) } + + context 'length is shorter than 12' do + let(:aws_account_id) { '1' * 11 } + + it { is_expected.to be_falsey } + end + + context 'length is longer than 12' do + let(:aws_account_id) { '1' * 13 } + + it { is_expected.to be_falsey } + end + + context 'has invalid characters' do + let(:aws_account_id) { '0123456789ab' } + + it { is_expected.to be_falsey } + end + + context 'account ID is valid' do + let(:aws_account_id) { '123456789012' } + + it { is_expected.to be_truthy } + end + + context 'account ID is valid, but contains hypens' do + let(:aws_account_id) { '1234-5678-9012' } + + it { is_expected.to be_truthy } + + it 'strips hyphens before validating' do + is_expected.to be_truthy + + expect(provider.aws_account_id).to eq('123456789012') + end + end + end + + context ':num_nodes' do + let(:provider) { build(:cluster_provider_aws, num_nodes: num_nodes) } + + context 'contains non-digit characters' do + let(:num_nodes) { 'A3' } + + it { is_expected.to be_falsey } + end + + context 'is blank' do + let(:num_nodes) { nil } + + it { is_expected.to be_falsey } + end + + context 'is less than 1' do + let(:num_nodes) { 0 } + + it { is_expected.to be_falsey } + end + + context 'is a positive integer' do + let(:num_nodes) { 3 } + + it { is_expected.to be_truthy } + end + end + end + + describe '#nullify_credentials' do + let(:provider) { create(:cluster_provider_aws, :scheduled) } + + subject { provider.nullify_credentials } + + before do + expect(provider.access_key_id).to be_present + expect(provider.secret_access_key).to be_present + end + + it 'removes access_key_id and secret_access_key' do + subject + + expect(provider.access_key_id).to be_nil + expect(provider.secret_access_key).to be_nil + end + end +end diff --git a/spec/models/clusters/providers/gcp_spec.rb b/spec/models/clusters/providers/gcp_spec.rb index 785db4febe0..c0aa3fb2c9d 100644 --- a/spec/models/clusters/providers/gcp_spec.rb +++ b/spec/models/clusters/providers/gcp_spec.rb @@ -6,6 +6,8 @@ describe Clusters::Providers::Gcp do it { is_expected.to belong_to(:cluster) } it { is_expected.to validate_presence_of(:zone) } + include_examples 'provider status', :cluster_provider_gcp + describe 'default_value_for' do let(:gcp) { build(:cluster_provider_gcp) } @@ -84,110 +86,67 @@ describe Clusters::Providers::Gcp do it { is_expected.not_to be_legacy_abac } end - describe '#state_machine' do - context 'when any => [:created]' do - let(:gcp) { build(:cluster_provider_gcp, :creating) } + describe '#api_client' do + subject { gcp.api_client } - before do - gcp.make_created - end + context 'when status is creating' do + let(:gcp) { build(:cluster_provider_gcp, :creating) } - it 'nullify access_token and operation_id' do - expect(gcp.access_token).to be_nil - expect(gcp.operation_id).to be_nil - expect(gcp).to be_created + it 'returns Cloud Platform API clinet' do + expect(subject).to be_an_instance_of(GoogleApi::CloudPlatform::Client) + expect(subject.access_token).to eq(gcp.access_token) end end - context 'when any => [:creating]' do - let(:gcp) { build(:cluster_provider_gcp) } - - context 'when operation_id is present' do - let(:operation_id) { 'operation-xxx' } - - before do - gcp.make_creating(operation_id) - end - - it 'sets operation_id' do - expect(gcp.operation_id).to eq(operation_id) - expect(gcp).to be_creating - end - end - - context 'when operation_id is nil' do - let(:operation_id) { nil } + context 'when status is created' do + let(:gcp) { build(:cluster_provider_gcp, :created) } - it 'raises an error' do - expect { gcp.make_creating(operation_id) } - .to raise_error('operation_id is required') - end - end + it { is_expected.to be_nil } end - context 'when any => [:errored]' do - let(:gcp) { build(:cluster_provider_gcp, :creating) } - let(:status_reason) { 'err msg' } - - it 'nullify access_token and operation_id' do - gcp.make_errored(status_reason) - - expect(gcp.access_token).to be_nil - expect(gcp.operation_id).to be_nil - expect(gcp.status_reason).to eq(status_reason) - expect(gcp).to be_errored - end - - context 'when status_reason is nil' do - let(:gcp) { build(:cluster_provider_gcp, :errored) } - - it 'does not set status_reason' do - gcp.make_errored(nil) + context 'when status is errored' do + let(:gcp) { build(:cluster_provider_gcp, :errored) } - expect(gcp.status_reason).not_to be_nil - end - end + it { is_expected.to be_nil } end end - describe '#on_creation?' do - subject { gcp.on_creation? } + describe '#nullify_credentials' do + let(:provider) { create(:cluster_provider_gcp, :creating) } - context 'when status is creating' do - let(:gcp) { create(:cluster_provider_gcp, :creating) } + subject { provider.nullify_credentials } - it { is_expected.to be_truthy } + before do + expect(provider.access_token).to be_present + expect(provider.operation_id).to be_present end - context 'when status is created' do - let(:gcp) { create(:cluster_provider_gcp, :created) } + it 'removes access_token and operation_id' do + subject - it { is_expected.to be_falsey } + expect(provider.access_token).to be_nil + expect(provider.operation_id).to be_nil end end - describe '#api_client' do - subject { gcp.api_client } - - context 'when status is creating' do - let(:gcp) { build(:cluster_provider_gcp, :creating) } + describe '#assign_operation_id' do + let(:provider) { create(:cluster_provider_gcp, :scheduled) } + let(:operation_id) { 'operation-123' } - it 'returns Cloud Platform API clinet' do - expect(subject).to be_an_instance_of(GoogleApi::CloudPlatform::Client) - expect(subject.access_token).to eq(gcp.access_token) - end - end + subject { provider.assign_operation_id(operation_id) } - context 'when status is created' do - let(:gcp) { build(:cluster_provider_gcp, :created) } + it 'sets operation_id' do + subject - it { is_expected.to be_nil } + expect(provider.operation_id).to eq(operation_id) end - context 'when status is errored' do - let(:gcp) { build(:cluster_provider_gcp, :errored) } + context 'operation ID is nil' do + let(:operation_id) { nil } - it { is_expected.to be_nil } + it 'raises an error' do + expect { subject }.to raise_error(ArgumentError, 'operation_id is required') + end end end end diff --git a/spec/support/shared_examples/models/clusters/providers/provider_status.rb b/spec/support/shared_examples/models/clusters/providers/provider_status.rb new file mode 100644 index 00000000000..507634d8ce7 --- /dev/null +++ b/spec/support/shared_examples/models/clusters/providers/provider_status.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +shared_examples 'provider status' do |factory| + describe 'state_machine' do + context 'when any => [:created]' do + let(:provider) { build(factory, :creating) } + + it 'nullifies API credentials' do + expect(provider).to receive(:nullify_credentials) + provider.make_created + + expect(provider).to be_created + end + end + + context 'when any => [:creating]' do + let(:provider) { build(factory) } + let(:operation_id) { 'operation-xxx' } + + it 'sets operation_id' do + expect(provider).to receive(:assign_operation_id).with(operation_id) + + provider.make_creating(operation_id) + end + end + + context 'when any => [:errored]' do + let(:provider) { build(factory, :creating) } + let(:status_reason) { 'err msg' } + + it 'removes access_token and operation_id' do + expect(provider).to receive(:nullify_credentials) + + provider.make_errored(status_reason) + + expect(provider.status_reason).to eq(status_reason) + expect(provider).to be_errored + end + + context 'when status_reason is nil' do + let(:provider) { build(factory, :errored) } + + it 'does not set status_reason' do + provider.make_errored(nil) + + expect(provider.status_reason).not_to be_nil + end + end + end + end + + describe '#on_creation?' do + subject { provider.on_creation? } + + context 'when status is creating' do + let(:provider) { create(factory, :creating) } + + it { is_expected.to be_truthy } + end + + context 'when status is created' do + let(:provider) { create(factory, :created) } + + it { is_expected.to be_falsey } + end + end +end |