From c471430a13c79a82555ee171fecdf45b882a3c19 Mon Sep 17 00:00:00 2001
From: Kamil Trzcinski <ayufan@ayufan.eu>
Date: Tue, 3 Oct 2017 23:26:26 +0200
Subject: [PATCH] Gcp::Cluster improvements

- introduce state machine
- use before transitions to clear data
- introduce namespace validation
---
 app/models/gcp/cluster.rb                    | 63 ++++++++++----------
 app/services/ci/integrate_cluster_service.rb | 10 +++-
 2 files changed, 42 insertions(+), 31 deletions(-)

diff --git a/app/models/gcp/cluster.rb b/app/models/gcp/cluster.rb
index 8b39bedad55..543505c24c2 100644
--- a/app/models/gcp/cluster.rb
+++ b/app/models/gcp/cluster.rb
@@ -22,49 +22,52 @@ module Gcp
       algorithm: 'aes-256-cbc'
 
     enum status: {
-      unknown: nil,
       scheduled: 1,
       creating: 2,
       created: 3,
       errored: 4
     }
 
+    state_machine :status, initial: :scheduled do
+      event :creating do
+        transition any - [:creating] => :creating
+      end
+
+      event :created do
+        transition any - [:created] => :created
+      end
+
+      event :errored do
+        transition any - [:errored] => :errored
+      end
+
+      before_transition any => [:errored, :created] do |cluster|
+        cluster.gcp_token = nil
+        cluster.gcp_operation_id = nil
+      end
+
+      before_transition any => [:errored] do |cluster|
+        status_reason = transition.args.first
+        cluster.status_reason = status_reason
+      end
+    end
+
     validates :gcp_project_id, presence: true
     validates :gcp_cluster_zone, presence: true
     validates :gcp_cluster_name, presence: true
     validates :gcp_cluster_size, presence: true,
               numericality: { only_integer: true, greater_than: 0 }
-    validate :restrict_modification, on: :update
-
-    def errored!(reason)
-      self.status = :errored
-      self.status_reason = reason
-      self.gcp_token = nil
-
-      save!(validate: false)
-    end
-
-    def creating!(gcp_operation_id)
-      self.status = :creating
-      self.gcp_operation_id = gcp_operation_id
 
-      save!(validate: false)
-    end
+    validates :project_namespace,
+      allow_blank: true,
+      length: 1..63,
+      format: {
+        with: Gitlab::Regex.kubernetes_namespace_regex,
+        message: Gitlab::Regex.kubernetes_namespace_regex_message
+      }
 
-    def created!(endpoint, ca_cert, kubernetes_token, username, password)
-      self.status = :created
-      self.enabled = true
-      self.endpoint = endpoint
-      self.ca_cert = ca_cert
-      self.kubernetes_token = kubernetes_token
-      self.username = username
-      self.password = password
-      self.service = project.find_or_initialize_service('kubernetes')
-      self.gcp_token = nil
-      self.gcp_operation_id = nil
-
-      save!
-    end
+    # if we do not do status transition we prevent change
+    validate :restrict_modification, on: :update, unless: :status_changed?
 
     def on_creation?
       scheduled? || creating?
diff --git a/app/services/ci/integrate_cluster_service.rb b/app/services/ci/integrate_cluster_service.rb
index c60d3722373..959c425fe74 100644
--- a/app/services/ci/integrate_cluster_service.rb
+++ b/app/services/ci/integrate_cluster_service.rb
@@ -2,7 +2,15 @@ module Ci
   class IntegrateClusterService
     def execute(cluster, endpoint, ca_cert, token, username, password)
       Gcp::Cluster.transaction do
-        cluster.created!(endpoint, ca_cert, token, username, password)
+        cluster.update!(
+          enabled: true,
+          endpoint: endpoint,
+          ca_cert: ca_cert,
+          kubernetes_token: token,
+          username: username,
+          password: password,
+          service: project.find_or_initialize_service('kubernetes'),
+          status_event: :created)
 
         cluster.service.update!(
           active: true,
-- 
2.30.9