Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
d0cff7f5
Commit
d0cff7f5
authored
Oct 23, 2017
by
Shinya Maeda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
This works
parent
e1d12ba9
Changes
24
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
422 additions
and
219 deletions
+422
-219
app/controllers/projects/clusters_controller.rb
app/controllers/projects/clusters_controller.rb
+16
-10
app/models/clusters/cluster.rb
app/models/clusters/cluster.rb
+30
-22
app/models/clusters/cluster_project.rb
app/models/clusters/cluster_project.rb
+0
-6
app/models/clusters/platforms/kubernetes.rb
app/models/clusters/platforms/kubernetes.rb
+23
-16
app/models/clusters/project.rb
app/models/clusters/project.rb
+8
-0
app/models/clusters/providers/gcp.rb
app/models/clusters/providers/gcp.rb
+14
-15
app/models/project.rb
app/models/project.rb
+2
-2
app/policies/clusters/cluster_policy.rb
app/policies/clusters/cluster_policy.rb
+2
-2
app/presenters/clusters/cluster_presenter.rb
app/presenters/clusters/cluster_presenter.rb
+2
-2
app/services/clusters/create_service.rb
app/services/clusters/create_service.rb
+30
-5
app/services/clusters/gcp/fetch_operation_service.rb
app/services/clusters/gcp/fetch_operation_service.rb
+3
-3
app/services/clusters/gcp/finalize_creation_service.rb
app/services/clusters/gcp/finalize_creation_service.rb
+17
-21
app/services/clusters/gcp/provision_service.rb
app/services/clusters/gcp/provision_service.rb
+26
-28
app/services/clusters/gcp/verify_provision_status_service.rb
app/services/clusters/gcp/verify_provision_status_service.rb
+9
-5
app/validators/cluster_name_validator.rb
app/validators/cluster_name_validator.rb
+24
-0
app/views/projects/clusters/_form.html.haml
app/views/projects/clusters/_form.html.haml
+24
-20
app/views/projects/clusters/show.html.haml
app/views/projects/clusters/show.html.haml
+3
-3
app/workers/cluster_provision_worker.rb
app/workers/cluster_provision_worker.rb
+1
-1
app/workers/wait_for_cluster_creation_worker.rb
app/workers/wait_for_cluster_creation_worker.rb
+1
-1
db/migrate/20171013094327_create_new_clusters_architectures.rb
...grate/20171013094327_create_new_clusters_architectures.rb
+9
-9
db/migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb.rb
..._migrate_gcp_clusters_to_new_clusters_architectures.rb.rb
+84
-0
db/schema.rb
db/schema.rb
+60
-1
lib/gitlab/gcp/model.rb
lib/gitlab/gcp/model.rb
+0
-13
spec/factories/gcp/cluster.rb
spec/factories/gcp/cluster.rb
+34
-34
No files found.
app/controllers/projects/clusters_controller.rb
View file @
d0cff7f5
...
...
@@ -27,11 +27,17 @@ class Projects::ClustersController < Projects::ApplicationController
end
def
new
@cluster
=
project
.
build_cluster
@cluster
=
Clusters
::
Cluster
.
new
(
platform_type: :kubernetes
,
provider_type: :gcp
).
tap
do
|
cluster
|
cluster
.
build_provider_gcp
cluster
.
build_platform_kubernetes
cluster
.
projects
<<
project
end
end
def
create
@cluster
=
C
i
::
CreateService
@cluster
=
C
lusters
::
CreateService
.
new
(
project
,
current_user
,
create_params
)
.
execute
(
token_in_session
)
...
...
@@ -58,7 +64,7 @@ class Projects::ClustersController < Projects::ApplicationController
end
def
update
C
i
::
UpdateCluster
Service
C
lusters
::
Update
Service
.
new
(
project
,
current_user
,
update_params
)
.
execute
(
cluster
)
...
...
@@ -89,16 +95,16 @@ class Projects::ClustersController < Projects::ApplicationController
def
create_params
params
.
require
(
:cluster
).
permit
(
:enabled
,
:name
,
:platform_type
,
:provider_type
,
kubernetes_platform
:
[
platform_kubernetes_attributes
:
[
:namespace
],
gcp_provider:
[
:project_id
,
:cluster_zone
,
:cluster_name
,
:cluster_size
,
provider_gcp_attributes:
[
:gcp_project_id
,
:zone
,
:num_nodes
,
:machine_type
])
end
...
...
@@ -106,7 +112,7 @@ class Projects::ClustersController < Projects::ApplicationController
def
update_params
params
.
require
(
:cluster
).
permit
(
:enabled
,
kubernetes_platform
:
[
platform_kubernetes_attributes
:
[
:namespace
])
end
...
...
app/models/clusters/cluster.rb
View file @
d0cff7f5
...
...
@@ -2,49 +2,46 @@ module Clusters
class
Cluster
<
ActiveRecord
::
Base
include
Presentable
self
.
table_name
=
'clusters'
belongs_to
:user
belongs_to
:service
enum
:platform_type
{
enum
platform_type:
{
kubernetes:
1
}
enum
:provider_type
{
enum
provider_type:
{
user:
0
,
gcp:
1
}
has_many
:cluster_projects
has_many
:projects
,
through: :cluster_projects
has_many
:cluster_projects
,
class_name:
'Clusters::Project'
has_many
:projects
,
through: :cluster_projects
,
class_name:
'::Project'
has_one
:
gcp_provider
has_one
:
kubernetes_platform
has_one
:
provider_gcp
,
class_name:
'Clusters::Providers::Gcp'
has_one
:
platform_kubernetes
,
class_name:
'Clusters::Platforms::Kubernetes'
accepts_nested_attributes_for
:
gcp_provider
accepts_nested_attributes_for
:
kubernetes_platform
accepts_nested_attributes_for
:
provider_gcp
accepts_nested_attributes_for
:
platform_kubernetes
validates
:kubernetes_platform
,
presence:
true
,
if: :kubernetes?
validates
:gcp_provider
,
presence:
true
,
if: :gcp?
validates
:name
,
cluster_name:
true
validate
:restrict_modification
,
on: :update
delegate
:status
,
to: :provider
,
allow_nil:
true
delegate
:status_reason
,
to: :provider
,
allow_nil:
true
def
restrict_modification
if
provider
&
.
on_creation?
errors
.
add
(
:base
,
"cannot modify during creation"
)
return
false
end
true
end
delegate
:status_name
,
to: :provider
,
allow_nil:
true
delegate
:on_creation?
,
to: :provider
,
allow_nil:
true
def
provider
return
gcp_provider
if
gcp?
return
provider_gcp
if
gcp?
end
def
platform
return
kubernetes_platform
if
kubernetes?
return
platform_kubernetes
if
kubernetes?
end
def
project
first_project
end
def
first_project
...
...
@@ -52,5 +49,16 @@ module Clusters
@first_project
=
projects
.
first
end
private
def
restrict_modification
if
provider
&
.
on_creation?
errors
.
add
(
:base
,
"cannot modify during creation"
)
return
false
end
true
end
end
end
app/models/clusters/cluster_project.rb
deleted
100644 → 0
View file @
e1d12ba9
module
Clusters
class
ClusterProject
<
ActiveRecord
::
Base
belongs_to
:cluster
belongs_to
:project
end
end
app/models/clusters/platforms/kubernetes.rb
View file @
d0cff7f5
...
...
@@ -4,11 +4,13 @@ module Clusters
include
Gitlab
::
Kubernetes
include
ReactiveCaching
self
.
table_name
=
'cluster_platforms_kubernetes'
TEMPLATE_PLACEHOLDER
=
'Kubernetes namespace'
.
freeze
self
.
reactive_cache_key
=
->
(
service
)
{
[
service
.
class
.
model_name
.
singular
,
service
.
project
_id
]
}
self
.
reactive_cache_key
=
->
(
kubernetes
)
{
[
kubernetes
.
class
.
model_name
.
singular
,
kubernetes
.
cluster
_id
]
}
belongs_to
:cluster
belongs_to
:cluster
,
inverse_of: :platform_kubernetes
,
class_name:
'Clusters::Cluster'
attr_encrypted
:password
,
mode: :per_attribute_iv
,
...
...
@@ -28,8 +30,8 @@ module Clusters
message:
Gitlab
::
Regex
.
kubernetes_namespace_regex_message
}
validates
:api_url
,
url:
true
,
presence:
true
validates
:token
,
presence:
true
validates
:api_url
,
url:
true
,
presence:
true
,
on: :update
validates
:token
,
presence:
true
,
on: :update
after_save
:clear_reactive_cache!
...
...
@@ -53,9 +55,9 @@ module Clusters
{
key:
'KUBECONFIG'
,
value:
config
,
public:
false
,
file:
true
}
]
if
ca_
pem
.
present?
variables
<<
{
key:
'KUBE_CA_PEM'
,
value:
ca_
pem
,
public:
true
}
variables
<<
{
key:
'KUBE_CA_PEM_FILE'
,
value:
ca_
pem
,
public:
true
,
file:
true
}
if
ca_
cert
.
present?
variables
<<
{
key:
'KUBE_CA_PEM'
,
value:
ca_
cert
,
public:
true
}
variables
<<
{
key:
'KUBE_CA_PEM_FILE'
,
value:
ca_
cert
,
public:
true
,
file:
true
}
end
variables
...
...
@@ -76,7 +78,7 @@ module Clusters
# Caches resources in the namespace so other calls don't need to block on
# network access
def
calculate_reactive_cache
return
unless
active?
&&
project
&&
!
project
.
pending_delete?
return
unless
active?
&&
cluster
.
project
&&
!
cluster
.
project
.
pending_delete?
# We may want to cache extra things in the future
{
pods:
read_pods
}
...
...
@@ -87,15 +89,16 @@ module Clusters
url:
api_url
,
namespace:
actual_namespace
,
token:
token
,
ca_pem:
ca_
pem
)
ca_pem:
ca_
cert
)
end
def
namespace_placeholder
default_namespace
||
TEMPLATE_PLACEHOLDER
end
def
default_namespace
"
#{
cluster
.
first_project
.
path
}
-
#{
cluster
.
first_project
.
id
}
"
if
cluster
.
first_project
def
default_namespace
(
project
=
nil
)
project
||=
cluster
&
.
project
"
#{
project
.
path
}
-
#{
project
.
id
}
"
if
project
end
def
read_secrets
...
...
@@ -120,9 +123,9 @@ module Clusters
def
kubeclient_ssl_options
opts
=
{
verify_ssl:
OpenSSL
::
SSL
::
VERIFY_PEER
}
if
ca_
pem
.
present?
if
ca_
cert
.
present?
opts
[
:cert_store
]
=
OpenSSL
::
X509
::
Store
.
new
opts
[
:cert_store
].
add_cert
(
OpenSSL
::
X509
::
Certificate
.
new
(
ca_
pem
))
opts
[
:cert_store
].
add_cert
(
OpenSSL
::
X509
::
Certificate
.
new
(
ca_
cert
))
end
opts
...
...
@@ -131,7 +134,11 @@ module Clusters
private
def
build_kubeclient!
(
api_path:
'api'
,
api_version:
'v1'
)
raise
"Incomplete settings"
unless
api_url
&&
actual_namespace
&&
token
raise
"Incomplete settings"
unless
api_url
&&
actual_namespace
unless
(
username
&&
password
)
||
token
raise
"Either username/password or token is required to access API"
end
::
Kubeclient
::
Client
.
new
(
join_api_url
(
api_path
),
...
...
@@ -143,7 +150,7 @@ module Clusters
end
def
kubeclient_auth_options
return
{
username:
username
,
password:
password
}
if
username
return
{
username:
username
,
password:
password
}
if
username
&&
password
return
{
bearer_token:
token
}
if
token
end
...
...
@@ -159,7 +166,7 @@ module Clusters
def
terminal_auth
{
token:
token
,
ca_pem:
ca_
pem
,
ca_pem:
ca_
cert
,
max_session_time:
current_application_settings
.
terminal_max_session_time
}
end
...
...
app/models/clusters/project.rb
0 → 100644
View file @
d0cff7f5
module
Clusters
class
Project
<
ActiveRecord
::
Base
self
.
table_name
=
'cluster_projects'
belongs_to
:cluster
,
inverse_of: :projects
,
class_name:
'Clusters::Cluster'
belongs_to
:project
,
inverse_of: :project
,
class_name:
'Project'
end
end
app/models/clusters/providers/gcp.rb
View file @
d0cff7f5
module
Clusters
module
Providers
class
Gcp
<
ActiveRecord
::
Base
belongs_to
:cluster
self
.
table_name
=
'cluster_providers_gcp'
default_value_for
:cluster_zone
,
'us-central1-a'
default_value_for
:cluster_size
,
3
belongs_to
:cluster
,
inverse_of: :provider_gcp
,
class_name:
'Clusters::Cluster'
default_value_for
:zone
,
'us-central1-a'
default_value_for
:num_nodes
,
3
default_value_for
:machine_type
,
'n1-standard-4'
attr_encrypted
:access_token
,
...
...
@@ -12,23 +14,16 @@ module Clusters
key:
Gitlab
::
Application
.
secrets
.
db_key_base
,
algorithm:
'aes-256-cbc'
validates
:project_id
,
validates
:
gcp_
project_id
,
length:
1
..
63
,
format:
{
with:
Gitlab
::
Regex
.
kubernetes_namespace_regex
,
message:
Gitlab
::
Regex
.
kubernetes_namespace_regex_message
}
validates
:cluster_name
,
length:
1
..
63
,
format:
{
with:
Gitlab
::
Regex
.
kubernetes_namespace_regex
,
message:
Gitlab
::
Regex
.
kubernetes_namespace_regex_message
}
validates
:cluster_zone
,
presence:
true
validates
:zone
,
presence:
true
validates
:
cluster_size
,
validates
:
num_nodes
,
presence:
true
,
numericality:
{
only_integer:
true
,
...
...
@@ -54,9 +49,13 @@ module Clusters
end
before_transition
any
=>
[
:errored
,
:created
]
do
|
provider
|
provider
.
token
=
nil
provider
.
access_
token
=
nil
provider
.
operation_id
=
nil
provider
.
save!
end
before_transition
any
=>
[
:creating
]
do
|
provider
,
transition
|
operation_id
=
transition
.
args
.
first
provider
.
operation_id
=
operation_id
if
operation_id
end
before_transition
any
=>
[
:errored
]
do
|
provider
,
transition
|
...
...
app/models/project.rb
View file @
d0cff7f5
...
...
@@ -178,8 +178,8 @@ class Project < ActiveRecord::Base
has_one
:project_feature
,
inverse_of: :project
has_one
:statistics
,
class_name:
'ProjectStatistics'
has_
many
:cluster_projects
,
class_name:
'Clusters::Cluster
Project'
has_one
:cluster
,
through: :cluster_project
s
has_
one
:cluster_project
,
class_name:
'Clusters::
Project'
has_one
:cluster
,
through: :cluster_project
,
class_name:
'Clusters::Cluster'
# Container repositories need to remove data from the container registry,
# which is not managed by the DB. Hence we're still using dependent: :destroy
...
...
app/policies/
gcp
/cluster_policy.rb
→
app/policies/
clusters
/cluster_policy.rb
View file @
d0cff7f5
module
Gcp
module
Clusters
class
ClusterPolicy
<
BasePolicy
alias_method
:cluster
,
:subject
delegate
{
@subject
.
project
}
delegate
{
cluster
.
project
}
rule
{
can?
(
:master_access
)
}.
policy
do
enable
:update_cluster
...
...
app/presenters/
gcp
/cluster_presenter.rb
→
app/presenters/
clusters
/cluster_presenter.rb
View file @
d0cff7f5
module
Gcp
module
Clusters
class
ClusterPresenter
<
Gitlab
::
View
::
Presenter
::
Delegated
presents
:cluster
def
gke_cluster_url
"https://console.cloud.google.com/kubernetes/clusters/details/
#{
gcp_cluster_zone
}
/
#{
gcp_cluster_name
}
"
"https://console.cloud.google.com/kubernetes/clusters/details/
#{
provider
.
zone
}
/
#{
name
}
"
if
gcp?
end
end
end
app/services/clusters/create_service.rb
View file @
d0cff7f5
module
Clusters
class
CreateService
<
BaseService
def
execute
(
access_token
)
params
[
'gcp_machine_type'
]
||=
GoogleApi
::
CloudPlatform
::
Client
::
DEFAULT_MACHINE_TYPE
attr_reader
:access_token
cluster_params
=
params
.
merge
(
user:
current_user
)
def
execute
(
access_token
)
@access_token
=
access_token
project
.
create_cluster
(
cluster_params
)
.
tap
do
|
cluster
|
create_cluster
.
tap
do
|
cluster
|
ClusterProvisionWorker
.
perform_async
(
cluster
.
id
)
if
cluster
.
persisted?
end
end
private
def
create_cluster
cluster
=
nil
ActiveRecord
::
Base
.
transaction
do
cluster
=
Clusters
::
Cluster
.
create!
(
cluster_params
)
cluster
.
projects
<<
project
end
cluster
rescue
ActiveRecord
::
RecordInvalid
=>
e
e
.
record
end
def
cluster_params
return
@cluster_params
if
defined?
(
@cluster_params
)
params
[
:provider_gcp_attributes
][
:machine_type
]
||=
GoogleApi
::
CloudPlatform
::
Client
::
DEFAULT_MACHINE_TYPE
params
[
:provider_gcp_attributes
][
:access_token
]
||=
access_token
@cluster_params
=
params
.
merge
(
user:
current_user
)
end
end
end
app/services/clusters/gcp/fetch_operation_service.rb
View file @
d0cff7f5
...
...
@@ -3,13 +3,13 @@ module Clusters
class
FetchOperationService
def
execute
(
provider
)
operation
=
provider
.
api_client
.
projects_zones_operations
(
provider
.
project_id
,
provider
.
cluster_
zone
,
provider
.
gcp_
project_id
,
provider
.
zone
,
provider
.
operation_id
)
yield
(
operation
)
if
block_given?
rescue
Google
::
Apis
::
ServerError
,
Google
::
Apis
::
ClientError
,
Google
::
Apis
::
AuthorizationError
=>
e
return
provider
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
provider
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
end
end
end
...
...
app/services/clusters/gcp/finalize_creation_service.rb
View file @
d0cff7f5
...
...
@@ -7,15 +7,14 @@ module Clusters
@provider
=
provider
configure_provider
configure_kubernetes_platform
request_kuberenetes_platform_token
configure_kubernetes
ActiveRecord
::
Base
.
transaction
do
kubernetes
_platform
.
updat
e!
kubernetes
.
sav
e!
provider
.
make_created!
end
rescue
Google
::
Apis
::
ServerError
,
Google
::
Apis
::
ClientError
,
Google
::
Apis
::
AuthorizationError
=>
e
return
cluster
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
cluster
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
rescue
ActiveRecord
::
RecordInvalid
=>
e
cluster
.
make_errored!
(
"Failed to configure GKE Cluster:
#{
e
.
message
}
"
)
end
...
...
@@ -26,23 +25,20 @@ module Clusters
provider
.
endpoint
=
gke_cluster
.
endpoint
end
def
configure_kubernetes
_platform
kubernetes
_platform
=
cluster
.
kubernetes_platform
kubernetes
_platform
.
api_url
=
'https://'
+
endpoint
kubernetes
_platform
.
ca_cert
=
Base64
.
decode64
(
gke_cluster
.
master_auth
.
cluster_ca_certificate
)
kubernetes
_platform
.
username
=
gke_cluster
.
master_auth
.
username
kubernetes
_platform
.
password
=
gke_cluster
.
master_auth
.
password
def
configure_kubernetes
kubernetes
.
api_url
=
'https://'
+
gke_cluster
.
endpoint
kubernetes
.
ca_cert
=
Base64
.
decode64
(
gke_cluster
.
master_auth
.
cluster_ca_certificate
)
kubernetes
.
username
=
gke_cluster
.
master_auth
.
username
kubernetes
.
password
=
gke_cluster
.
master_auth
.
password
kubernetes
.
token
=
request_kuberenetes_token
end
def
request_kuberenetes_
platform_
token
kubernetes
_platform
.
read_secrets
.
each
do
|
secret
|
def
request_kuberenetes_token
kubernetes
.
read_secrets
.
each
do
|
secret
|
name
=
secret
.
dig
(
'metadata'
,
'name'
)
if
/default-token/
=~
name
token_base64
=
secret
.
dig
(
'data'
,
'token'
)
if
token_base64
kubernetes_platform
.
token
=
Base64
.
decode64
(
token_base64
)
break
end
return
Base64
.
decode64
(
token_base64
)
if
token_base64
end
end
end
...
...
@@ -50,16 +46,16 @@ module Clusters
def
gke_cluster
@gke_cluster
||=
provider
.
api_client
.
projects_zones_clusters_get
(
provider
.
gcp_project_id
,
provider
.
gcp_cluster_
zone
,
provider
.
gcp_cluster_
name
)
provider
.
zone
,
cluster
.
name
)
end
def
cluster
provider
.
cluster
@cluster
||=
provider
.
cluster
end
def
kubernetes
_platform
cluster
.
kubernetes_platform
def
kubernetes
@kubernetes
||=
cluster
.
platform_kubernetes
end
end
end
...
...
app/services/clusters/gcp/provision_service.rb
View file @
d0cff7f5
...
...
@@ -6,43 +6,41 @@ module Clusters
def
execute
(
provider
)
@provider
=
provider
unless
operation
.
status
==
'RUNNING'
||
operation
.
status
==
'PENDING'
return
provider
.
make_errored!
(
"Operation status is unexpected;
#{
operation
.
status_message
}
"
)
end
provider
.
operation_id
=
operation_id
unless
provider
.
operation_id
return
provider
.
make_errored!
(
'Can not find operation_id from self_link'
)
end
if
provider
.
make_creating
get_operation_id
do
|
operation_id
|
if
provider
.
make_creating
(
operation_id
)
WaitForClusterCreationWorker
.
perform_in
(
WaitForClusterCreationWorker
::
INITIAL_INTERVAL
,
provider
.
id
)
Clusters
::
Gcp
::
VerifyProvisionStatusService
::
INITIAL_INTERVAL
,
provider
.
id
)
else
return
provider
.
make_errored!
(
"Failed to update provider record;
#{
provider
.
errors
}
"
)
provider
.
make_errored!
(
"Failed to update provider record;
#{
provider
.
errors
}
"
)
end
end
rescue
Google
::
Apis
::
ServerError
,
Google
::
Apis
::
ClientError
,
Google
::
Apis
::
AuthorizationError
=>
e
return
provider
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
end
private
def
operation_id
api_client
.
parse_operation_id
(
operation
.
self_link
)
def
get_operation_id
operation
=
provider
.
api_client
.
projects_zones_clusters_create
(
provider
.
gcp_project_id
,
provider
.
zone
,
provider
.
cluster
.
name
,
provider
.
num_nodes
,
machine_type:
provider
.
machine_type
)
unless
operation
.
status
==
'PENDING'
||
operation
.
status
==
'RUNNING'
return
provider
.
make_errored!
(
"Operation status is unexpected;
#{
operation
.
status_message
}
"
)
end
def
operation
@operation
||=
api_client
.
projects_zones_providers_create
(
provider
.
project_id
,
provider
.
provider_zone
,
provider
.
provider_name
,
provider
.
provider_size
,
machine_type:
provider
.
machine_type
)
operation_id
=
provider
.
api_client
.
parse_operation_id
(
operation
.
self_link
)
unless
operation_id
return
provider
.
make_errored!
(
'Can not find operation_id from self_link'
)
end
def
api_client
provider
.
api_client
yield
(
operation_id
)
rescue
Google
::
Apis
::
ServerError
,
Google
::
Apis
::
ClientError
,
Google
::
Apis
::
AuthorizationError
=>
e
provider
.
make_errored!
(
"Failed to request to CloudPlatform;
#{
e
.
message
}
"
)
end
end
end
...
...
app/services/clusters/gcp/verify_provision_status_service.rb
View file @
d0cff7f5
...
...
@@ -12,7 +12,7 @@ module Clusters
request_operation
do
|
operation
|
case
operation
.
status
when
'RUNNING'
when
'
PENDING'
,
'
RUNNING'
continue_creation
(
operation
)
when
'DONE'
finalize_creation
...
...
@@ -25,11 +25,15 @@ module Clusters
private
def
continue_creation
(
operation
)
if
TIMEOUT
<
Time
.
now
.
utc
-
operation
.
start_time
.
to_time
.
utc
return
provider
.
make_errored!
(
"Cluster creation time exceeds timeout;
#{
TIMEOUT
}
"
)
if
elapsed_time_from_creation
(
operation
)
<
TIMEOUT
WaitForClusterCreationWorker
.
perform_in
(
EAGER_INTERVAL
,
provider
.
cluster_id
)
else
provider
.
make_errored!
(
"Cluster creation time exceeds timeout;
#{
TIMEOUT
}
"
)
end
end
WaitForClusterCreationWorker
.
perform_in
(
EAGER_INTERVAL
,
provider
.
cluster_id
)
def
elapsed_time_from_creation
(
operation
)
Time
.
now
.
utc
-
operation
.
start_time
.
to_time
.
utc
end
def
finalize_creation
...
...
@@ -37,7 +41,7 @@ module Clusters
end
def
request_operation
(
&
blk
)
Clusters
::
FetchGcp
OperationService
.
new
.
execute
(
provider
,
&
blk
)
Clusters
::
Gcp
::
Fetch
OperationService
.
new
.
execute
(
provider
,
&
blk
)
end
end
end
...
...
app/validators/cluster_name_validator.rb
0 → 100644
View file @
d0cff7f5
# ClusterNameValidator
#
# Custom validator for ClusterName.
class
ClusterNameValidator
<
ActiveModel
::
EachValidator
def
validate_each
(
record
,
attribute
,
value
)
if
record
.
user?
unless
value
.
present?
record
.
errors
.
add
(
attribute
,
" has to be present"
)
end
elsif
record
.
gcp?
if
record
.
persisted?
&&
record
.
name
!=
value
record
.
errors
.
add
(
attribute
,
" can not be changed because it's synchronized with provider"
)
end
unless
value
.
length
>=
1
&&
value
.
length
<=
63
record
.
errors
.
add
(
attribute
,
" is invalid syntax"
)
end
unless
value
=~
Gitlab
::
Regex
.
kubernetes_namespace_regex
record
.
errors
.
add
(
attribute
,
Gitlab
::
Regex
.
kubernetes_namespace_regex_message
)
end
end
end
end
app/views/projects/clusters/_form.html.haml
View file @
d0cff7f5
...
...
@@ -4,34 +4,38 @@
-
link_to_help_page
=
link_to
(
s_
(
'ClusterIntegration|help page'
),
help_page_path
(
'user/project/clusters/index'
),
target:
'_blank'
,
rel:
'noopener noreferrer'
)
=
s_
(
'ClusterIntegration|Read our %{link_to_help_page} on cluster integration.'
).
html_safe
%
{
link_to_help_page:
link_to_help_page
}
=
form_for
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
@cluster
]
do
|
field
|
=
form_for
@cluster
,
url:
namespace_project_clusters_path
(
@project
.
namespace
,
@project
,
@cluster
),
as: :cluster
do
|
field
|
=
field
.
hidden_field
:platform_type
,
:value
=>
'kubernetes'
=
field
.
hidden_field
:provider_type
,
:value
=>
'gcp'
=
form_errors
(
@cluster
)
.form-group
=
field
.
label
:
gcp_cluster_
name
,
s_
(
'ClusterIntegration|Cluster name'
)
=
field
.
text_field
:
gcp_cluster_
name
,
class:
'form-control'
=
field
.
label
:name
,
s_
(
'ClusterIntegration|Cluster name'
)
=
field
.
text_field
:name
,
class:
'form-control'
=
field
.
fields_for
:provider_gcp
,
@cluster
.
provider_gcp
do
|
provider_gcp_field
|
.form-group
=
field
.
label
:gcp_project_id
,
s_
(
'ClusterIntegration|Google Cloud Platform project ID'
)
=
provider_gcp_
field
.
label
:gcp_project_id
,
s_
(
'ClusterIntegration|Google Cloud Platform project ID'
)
=
link_to
(
s_
(
'ClusterIntegration|See your projects'
),
'https://console.cloud.google.com/home/dashboard'
,
target:
'_blank'
,
rel:
'noopener noreferrer'
)
=
field
.
text_field
:gcp_project_id
,
class:
'form-control'
=
provider_gcp_
field
.
text_field
:gcp_project_id
,
class:
'form-control'
.form-group
=
field
.
label
:gcp_cluster_
zone
,
s_
(
'ClusterIntegration|Zone'
)
=
provider_gcp_field
.
label
:
zone
,
s_
(
'ClusterIntegration|Zone'
)
=
link_to
(
s_
(
'ClusterIntegration|See zones'
),
'https://cloud.google.com/compute/docs/regions-zones/regions-zones'
,
target:
'_blank'
,
rel:
'noopener noreferrer'
)
=
field
.
text_field
:gcp_cluster_
zone
,
class:
'form-control'
,
placeholder:
'us-central1-a'
=
provider_gcp_field
.
text_field
:
zone
,
class:
'form-control'
,
placeholder:
'us-central1-a'
.form-group
=
field
.
label
:gcp_cluster_size
,
s_
(
'ClusterIntegration|Number of nodes'
)
=
field
.
text_field
:gcp_cluster_size
,
class:
'form-control'
,
placeholder:
'3'
=
provider_gcp_field
.
label
:num_nodes
,
s_
(
'ClusterIntegration|Number of nodes'
)
=
provider_gcp_field
.
text_field
:num_nodes
,
class:
'form-control'
,
placeholder:
'3'
.form-group
=
field
.
label
:gcp_
machine_type
,
s_
(
'ClusterIntegration|Machine type'
)
=
provider_gcp_field
.
label
:
machine_type
,
s_
(
'ClusterIntegration|Machine type'
)
=
link_to
(
s_
(
'ClusterIntegration|See machine types'
),
'https://cloud.google.com/compute/docs/machine-types'
,
target:
'_blank'
,
rel:
'noopener noreferrer'
)
=
field
.
text_field
:gcp_
machine_type
,
class:
'form-control'
,
placeholder:
'n1-standard-4'
=
provider_gcp_field
.
text_field
:
machine_type
,
class:
'form-control'
,
placeholder:
'n1-standard-4'
=
field
.
fields_for
:platform_kubernetes
,
@cluster
.
platform_kubernetes
do
|
platform_kubernetes_field
|
.form-group
=
field
.
label
:project_
namespace
,
s_
(
'ClusterIntegration|Project namespace (optional, unique)'
)
=
field
.
text_field
:project_namespace
,
class:
'form-control'
,
placeholder:
@cluster
.
project_namespace_placeholder
=
platform_kubernetes_field
.
label
:
namespace
,
s_
(
'ClusterIntegration|Project namespace (optional, unique)'
)
=
platform_kubernetes_field
.
text_field
:namespace
,
class:
'form-control'
,
placeholder:
@cluster
.
platform_kubernetes
.
default_namespace
(
@project
)
.form-group
=
field
.
submit
s_
(
'ClusterIntegration|Create cluster'
),
class:
'btn btn-save'
app/views/projects/clusters/show.html.haml
View file @
d0cff7f5
...
...
@@ -33,7 +33,7 @@
-
else
=
s_
(
'ClusterIntegration|Cluster integration is disabled for this project.'
)
=
form_for
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
@cluster
]
do
|
field
|
=
form_for
@cluster
,
url:
namespace_project_cluster_path
(
@project
.
namespace
,
@project
,
@cluster
),
as: :cluster
do
|
field
|
=
form_errors
(
@cluster
)
.form-group.append-bottom-20
%label
.append-bottom-10
...
...
@@ -62,9 +62,9 @@
%label
.append-bottom-10
{
for:
'cluter-name'
}
=
s_
(
'ClusterIntegration|Cluster name'
)
.input-group
%input
.form-control.cluster-name
{
value:
@cluster
.
gcp_cluster_
name
,
disabled:
true
}
%input
.form-control.cluster-name
{
value:
@cluster
.
name
,
disabled:
true
}
%span
.input-group-addon.clipboard-addon
=
clipboard_button
(
text:
@cluster
.
gcp_cluster_
name
,
title:
s_
(
'ClusterIntegration|Copy cluster name'
))
=
clipboard_button
(
text:
@cluster
.
name
,
title:
s_
(
'ClusterIntegration|Copy cluster name'
))
%section
.settings
#js-cluster-advanced-settings
.settings-header
...
...
app/workers/cluster_provision_worker.rb
View file @
d0cff7f5
...
...
@@ -4,7 +4,7 @@ class ClusterProvisionWorker
def
perform
(
cluster_id
)
Clusters
::
Cluster
.
find_by_id
(
cluster_id
).
try
do
|
cluster
|
cluster
.
gcp_provider
.
try
do
|
provider
|
cluster
.
provider_gcp
.
try
do
|
provider
|
Clusters
::
Gcp
::
ProvisionService
.
new
.
execute
(
provider
)
end
end
...
...
app/workers/wait_for_cluster_creation_worker.rb
View file @
d0cff7f5
...
...
@@ -4,7 +4,7 @@ class WaitForClusterCreationWorker
def
perform
(
cluster_id
)
Clusters
::
Cluster
.
find_by_id
(
cluster_id
).
try
do
|
cluster
|
cluster
.
gcp_provider
.
try
do
|
provider
|
cluster
.
provider_gcp
.
try
do
|
provider
|
Clusters
::
Gcp
::
VerifyProvisionStatusService
.
new
.
execute
(
provider
)
end
end
...
...
db/migrate/20171013094327_create_
cluster
s.rb
→
db/migrate/20171013094327_create_
new_clusters_architecture
s.rb
View file @
d0cff7f5
class
Create
GcpCluster
s
<
ActiveRecord
::
Migration
class
Create
NewClustersArchitecture
s
<
ActiveRecord
::
Migration
DOWNTIME
=
false
def
change
...
...
@@ -6,6 +6,7 @@ class CreateGcpClusters < ActiveRecord::Migration
t
.
references
:user
,
foreign_key:
{
on_delete: :nullify
}
t
.
boolean
:enabled
,
default:
true
t
.
string
:name
,
null:
false
# If manual, read-write. If gcp, read-only.
t
.
integer
:provider_type
,
null:
false
t
.
integer
:platform_type
,
null:
false
...
...
@@ -15,14 +16,14 @@ class CreateGcpClusters < ActiveRecord::Migration
end
create_table
:cluster_projects
do
|
t
|
t
.
references
:project
,
null:
false
,
index:
{
unique:
true
}
,
foreign_key:
{
on_delete: :cascade
}
t
.
references
:cluster
,
null:
false
,
index:
{
unique:
true
}
,
foreign_key:
{
on_delete: :cascade
}
t
.
references
:project
,
null:
false
,
index:
true
,
foreign_key:
{
on_delete: :cascade
}
t
.
references
:cluster
,
null:
false
,
index:
true
,
foreign_key:
{
on_delete: :cascade
}
t
.
datetime_with_timezone
:created_at
,
null:
false
t
.
datetime_with_timezone
:updated_at
,
null:
false
end
create_table
:cluster_
kubernetes_platform
s
do
|
t
|
create_table
:cluster_
platforms_kubernete
s
do
|
t
|
t
.
references
:cluster
,
null:
false
,
index:
{
unique:
true
},
foreign_key:
{
on_delete: :cascade
}
t
.
string
:api_url
...
...
@@ -41,16 +42,15 @@ class CreateGcpClusters < ActiveRecord::Migration
t
.
datetime_with_timezone
:updated_at
,
null:
false
end
create_table
:cluster_
gcp_providers
do
|
t
|
create_table
:cluster_
providers_gcp
do
|
t
|
t
.
references
:cluster
,
null:
false
,
index:
{
unique:
true
},
foreign_key:
{
on_delete: :cascade
}
t
.
integer
:status
t
.
text
:status_reason
t
.
string
:project_id
,
null:
false
t
.
string
:cluster_zone
,
null:
false
t
.
string
:cluster_name
,
null:
false
t
.
integer
:cluster_size
,
null:
false
t
.
string
:gcp_project_id
,
null:
false
t
.
string
:zone
,
null:
false
t
.
integer
:num_nodes
,
null:
false
t
.
string
:machine_type
t
.
string
:operation_id
...
...
db/migrate/20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb.rb
0 → 100644
View file @
d0cff7f5
class
MigrateGcpClustersToNewClustersArchitectures
<
ActiveRecord
::
Migration
DOWNTIME
=
false
def
up
# TODO: Chnage to something reaistic
ActiveRecord
::
Base
.
connection
.
select_rows
(
'SELECT * from gcp_clusters;'
).
each
do
|
old_cluster
|
id
=
old_cluster
[
0
]
project_id
=
old_cluster
[
1
]
user_id
=
old_cluster
[
2
]
service_id
=
old_cluster
[
3
]
status
=
old_cluster
[
4
]
gcp_cluster_size
=
old_cluster
[
5
]
created_at
=
old_cluster
[
6
]
updated_at
=
old_cluster
[
7
]
enabled
=
old_cluster
[
8
]
status_reason
=
old_cluster
[
9
]
project_namespace
=
old_cluster
[
10
]
endpoint
=
old_cluster
[
11
]
ca_cert
=
old_cluster
[
12
]
encrypted_kubernetes_token
=
old_cluster
[
13
]
encrypted_kubernetes_token_iv
=
old_cluster
[
14
]
username
=
old_cluster
[
15
]
encrypted_password
=
old_cluster
[
16
]
encrypted_password_iv
=
old_cluster
[
17
]
gcp_project_id
=
old_cluster
[
18
]
gcp_cluster_zone
=
old_cluster
[
19
]
gcp_cluster_name
=
old_cluster
[
20
]
gcp_machine_type
=
old_cluster
[
21
]
gcp_operation_id
=
old_cluster
[
22
]
encrypted_gcp_token
=
old_cluster
[
23
]
encrypted_gcp_token_iv
=
old_cluster
[
24
]
cluster
=
Clusters
::
Cluster
.
create!
(
user_id:
user_id
,
enabled:
enabled
,
name:
gcp_cluster_name
,
provider_type: :gcp
,
platform_type: :kubernetes
,
created_at:
created_at
,
updated_at:
updated_at
)
Clusters
::
Project
.
create!
(
cluster:
cluster
,
project_id:
project_id
,
created_at:
created_at
,
updated_at:
updated_at
)
Clusters
::
Platforms
::
Kubernetes
.
create!
(
cluster:
cluster
,
api_url:
'https://'
+
endpoint
,
ca_cert:
ca_cert
,
namespace:
project_namespace
,
username:
username
,
encrypted_password:
encrypted_password
,
encrypted_password_iv:
encrypted_password_iv
,
encrypted_token:
encrypted_kubernetes_token
,
encrypted_token_iv:
encrypted_kubernetes_token_iv
,
created_at:
created_at
,
updated_at:
updated_at
)
Clusters
::
Providers
::
Gcp
.
create!
(
cluster:
cluster
,
status:
status
,
status_reason:
status_reason
,
gcp_project_id:
gcp_project_id
,
zone:
gcp_cluster_zone
,
num_nodes:
gcp_cluster_size
,
machine_type:
gcp_machine_type
,
operation_id:
gcp_operation_id
,
endpoint:
endpoint
,
encrypted_access_token:
encrypted_gcp_token
,
encrypted_access_token_iv:
encrypted_gcp_token_iv
,
created_at:
created_at
,
updated_at:
updated_at
)
end
end
def
down
Clusters
::
Cluster
.
delete_all
Clusters
::
Project
.
delete_all
Clusters
::
Providers
::
Gcp
.
delete_all
Clusters
::
Platforms
::
Kubernetes
.
delete_all
end
end
db/schema.rb
View file @
d0cff7f5
...
...
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord
::
Schema
.
define
(
version:
2017101
2101043
)
do
ActiveRecord
::
Schema
.
define
(
version:
2017101
3104327
)
do
# These are extensions that must be enabled in order to support this database
enable_extension
"plpgsql"
...
...
@@ -460,6 +460,60 @@ ActiveRecord::Schema.define(version: 20171012101043) do
add_index
"ci_variables"
,
[
"project_id"
,
"key"
,
"environment_scope"
],
name:
"index_ci_variables_on_project_id_and_key_and_environment_scope"
,
unique:
true
,
using: :btree
create_table
"cluster_platforms_kubernetes"
,
force: :cascade
do
|
t
|
t
.
integer
"cluster_id"
,
null:
false
t
.
string
"api_url"
t
.
text
"ca_cert"
t
.
string
"namespace"
t
.
string
"username"
t
.
text
"encrypted_password"
t
.
string
"encrypted_password_iv"
t
.
text
"encrypted_token"
t
.
string
"encrypted_token_iv"
t
.
datetime
"created_at"
,
null:
false
t
.
datetime
"updated_at"
,
null:
false
end
add_index
"cluster_platforms_kubernetes"
,
[
"cluster_id"
],
name:
"index_cluster_platforms_kubernetes_on_cluster_id"
,
unique:
true
,
using: :btree
create_table
"cluster_projects"
,
force: :cascade
do
|
t
|
t
.
integer
"project_id"
,
null:
false
t
.
integer
"cluster_id"
,
null:
false
t
.
datetime
"created_at"
,
null:
false
t
.
datetime
"updated_at"
,
null:
false
end
add_index
"cluster_projects"
,
[
"cluster_id"
],
name:
"index_cluster_projects_on_cluster_id"
,
using: :btree
add_index
"cluster_projects"
,
[
"project_id"
],
name:
"index_cluster_projects_on_project_id"
,
using: :btree
create_table
"cluster_providers_gcp"
,
force: :cascade
do
|
t
|
t
.
integer
"cluster_id"
,
null:
false
t
.
integer
"status"
t
.
text
"status_reason"
t
.
string
"gcp_project_id"
,
null:
false
t
.
string
"zone"
,
null:
false
t
.
integer
"num_nodes"
,
null:
false
t
.
string
"machine_type"
t
.
string
"operation_id"
t
.
string
"endpoint"
t
.
text
"encrypted_access_token"
t
.
string
"encrypted_access_token_iv"
t
.
datetime
"created_at"
,
null:
false
t
.
datetime
"updated_at"
,
null:
false
end
add_index
"cluster_providers_gcp"
,
[
"cluster_id"
],
name:
"index_cluster_providers_gcp_on_cluster_id"
,
unique:
true
,
using: :btree
create_table
"clusters"
,
force: :cascade
do
|
t
|
t
.
integer
"user_id"
t
.
boolean
"enabled"
,
default:
true
t
.
string
"name"
,
null:
false
t
.
integer
"provider_type"
,
null:
false
t
.
integer
"platform_type"
,
null:
false
t
.
datetime
"created_at"
,
null:
false
t
.
datetime
"updated_at"
,
null:
false
end
create_table
"container_repositories"
,
force: :cascade
do
|
t
|
t
.
integer
"project_id"
,
null:
false
t
.
string
"name"
,
null:
false
...
...
@@ -1808,6 +1862,11 @@ ActiveRecord::Schema.define(version: 20171012101043) do
add_foreign_key
"ci_triggers"
,
"projects"
,
name:
"fk_e3e63f966e"
,
on_delete: :cascade
add_foreign_key
"ci_triggers"
,
"users"
,
column:
"owner_id"
,
name:
"fk_e8e10d1964"
,
on_delete: :cascade
add_foreign_key
"ci_variables"
,
"projects"
,
name:
"fk_ada5eb64b3"
,
on_delete: :cascade
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_gcp"
,
"clusters"
,
on_delete: :cascade
add_foreign_key
"clusters"
,
"users"
,
on_delete: :nullify
add_foreign_key
"container_repositories"
,
"projects"
add_foreign_key
"deploy_keys_projects"
,
"projects"
,
name:
"fk_58a901ca7e"
,
on_delete: :cascade
add_foreign_key
"deployments"
,
"projects"
,
name:
"fk_b9a3851b82"
,
on_delete: :cascade
...
...
lib/gitlab/gcp/model.rb
deleted
100644 → 0
View file @
e1d12ba9
module
Gitlab
module
Gcp
module
Model
def
table_name_prefix
"gcp_"
end
def
model_name
@model_name
||=
ActiveModel
::
Name
.
new
(
self
,
nil
,
self
.
name
.
split
(
"::"
).
last
)
end
end
end
end
spec/factories/gcp/cluster.rb
View file @
d0cff7f5
FactoryGirl
.
define
do
factory
:gcp_cluster
,
class:
Gcp
::
Cluster
do
project
user
enabled
true
gcp_project_id
'gcp-project-12345'
gcp_cluster_name
'test-cluster'
gcp_cluster_zone
'us-central1-a'
gcp_cluster_size
1
gcp_machine_type
'n1-standard-4'
#
FactoryGirl.define do
#
factory :gcp_cluster, class: Gcp::Cluster do
#
project
#
user
#
enabled true
#
gcp_project_id 'gcp-project-12345'
#
gcp_cluster_name 'test-cluster'
#
gcp_cluster_zone 'us-central1-a'
#
gcp_cluster_size 1
#
gcp_machine_type 'n1-standard-4'
trait
:with_kubernetes_service
do
after
(
:create
)
do
|
cluster
,
evaluator
|
create
(
:kubernetes_service
,
project:
cluster
.
project
).
tap
do
|
service
|
cluster
.
update
(
service:
service
)
end
end
end
#
trait :with_kubernetes_service do
#
after(:create) do |cluster, evaluator|
#
create(:kubernetes_service, project: cluster.project).tap do |service|
#
cluster.update(service: service)
#
end
#
end
#
end
trait
:custom_project_namespace
do
project_namespace
'sample-app'
end
#
trait :custom_project_namespace do
#
project_namespace 'sample-app'
#
end
trait
:created_on_gke
do
status_event
:make_created
endpoint
'111.111.111.111'
ca_cert
'xxxxxx'
kubernetes_token
'xxxxxx'
username
'xxxxxx'
password
'xxxxxx'
end
#
trait :created_on_gke do
#
status_event :make_created
#
endpoint '111.111.111.111'
#
ca_cert 'xxxxxx'
#
kubernetes_token 'xxxxxx'
#
username 'xxxxxx'
#
password 'xxxxxx'
#
end
trait
:errored
do
status_event
:make_errored
status_reason
'general error'
end
end
end
#
trait :errored do
#
status_event :make_errored
#
status_reason 'general error'
#
end
#
end
#
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment