diff options
-rw-r--r-- | app/controllers/google_api/authorizations_controller.rb | 27 | ||||
-rw-r--r-- | app/controllers/projects/clusters_controller.rb | 102 | ||||
-rw-r--r-- | app/models/ci/cluster.rb | 14 | ||||
-rw-r--r-- | app/models/project.rb | 1 | ||||
-rw-r--r-- | app/views/projects/clusters/edit.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/clusters/new.html.haml | 17 | ||||
-rw-r--r-- | config/routes.rb | 1 | ||||
-rw-r--r-- | config/routes/google_api.rb | 5 | ||||
-rw-r--r-- | config/routes/project.rb | 7 | ||||
-rw-r--r-- | db/migrate/20170924094327_create_ci_clusters.rb | 29 | ||||
-rw-r--r-- | db/schema.rb | 68 | ||||
-rw-r--r-- | lib/google_api/authentication.rb | 51 | ||||
-rw-r--r-- | lib/google_api/cloud_platform/client.rb | 24 |
13 files changed, 323 insertions, 25 deletions
diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb new file mode 100644 index 00000000000..e99c38025b8 --- /dev/null +++ b/app/controllers/google_api/authorizations_controller.rb @@ -0,0 +1,27 @@ +module GoogleApi + class AuthorizationsController < ApplicationController + # callback_google_api_authorizations GET|POST /google_api/authorizations/callback(.:format) google_api/authorizations#callback + ## + # TODO: + # - Is it ok to use both "http://localhost:3000/google_api/authorizations/callback"(For login) and "http://localhost:3000/google_api/authorizations/callback"(For API token) + def callback + session[access_token_key] = api_client.get_token(params[:code]) + + if params[:state] + redirect_to params[:state] + else + redirect_to root_url + end + end + + def api_client + @api_client ||= + GoogleApi::Authentication.new(nil, callback_google_api_authorizations_url) + end + + def access_token_key + # :"#{api_client.scope}_access_token" + :"hoge_access_token" # TODO: + end + end +end diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb new file mode 100644 index 00000000000..5c9319f661a --- /dev/null +++ b/app/controllers/projects/clusters_controller.rb @@ -0,0 +1,102 @@ +class Projects::ClustersController < Projects::ApplicationController + # before_action :authenticate_google_api + before_action :cluster + + # before_action :authorize_admin_clusters! # TODO: Authentication + + def index + if cluster + redirect_to action: 'edit' + else + redirect_to action: 'new' + end + end + + ## + # TODO: + # - Show form for "Create on Google Container Engine" + # - Show form for "Use existing kubernets cluster" + # - If user has not authroized yet, Show "Sign in with Google" button + # - If user has already authroized, Skip "Sign in with Google" button + # - user.is_authenticated_for_gcp? + # - user.authenticate_for_gcp! + # - Create this module which can be used from view + def new + unless session[access_token_key] + @authorize_url = api_client.authorize_url + end + end + + ## + # TODO: + # - If create on GKE, Use Google::Apis::ContainerV1::ContainerService + # - If create manually, save in db (Prob, Project > Setting) + # - Dry up with Service + def create + redirect_to action: 'index' + end + + # TODO: Show results/status. Edits Swtich for enable/disable. + # If created with GKE, non-editable form. enable/disable switch. + # If created manually, editable form. enable/disable switch. + # GKE params are on-off swtich + # Manul params are on-off swtich, Endpoint, CACert, k8s Token, Proj namespace. + def edit + unless session[access_token_key] + @authorize_url = api_client.authorize_url + end + end + + def update + cluster.update(schedule_params) + render :edit + end + + # In presenter + # TODO: Generate a link to the cluster on GKE + + def gcp_projects + # api_client.blah + # TODO: Return all avaiable GCP Projects. + # TODO: Return json + # TODO: Dry with concern + end + + def gke_zones + # api_client.blah + # TODO: Return all avaiable zones on GKE. + # TODO: Return json + # TODO: Dry with concern + end + + private + + # def authenticate_google_api + # if cluster&.on_gke? && session[access_token_key].blank? + # redirect_to api_client.authorize_url(callback_import_url) + # end + # end + + def cluster + # Each project has only one cluster, for now. In the future iteraiton, we'll support multiple clusters + @cluster ||= project.clusters.first + end + + def cluster_params + params.require(:cluster).permit(:aaa) + end + + def api_client + @api_client ||= + GoogleApi::CloudPlatform::Client.new( + session[access_token_key], + callback_google_api_authorizations_url, + state: namespace_project_clusters_url.to_s + ) + end + + def access_token_key + # :"#{api_client.scope}_access_token" + :"hoge_access_token" # TODO: + end +end diff --git a/app/models/ci/cluster.rb b/app/models/ci/cluster.rb new file mode 100644 index 00000000000..2e614a1a373 --- /dev/null +++ b/app/models/ci/cluster.rb @@ -0,0 +1,14 @@ +module Ci + class Cluster < ActiveRecord::Base + extend Gitlab::Ci::Model + + belongs_to :project + belongs_to :owner, class_name: 'User' + + enum creation_type: { + unknown: nil, + on_gke: 1, + manual: 2 + } + end +end diff --git a/app/models/project.rb b/app/models/project.rb index f7221e4f3b2..6b896746864 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -171,6 +171,7 @@ class Project < ActiveRecord::Base has_many :commit_statuses has_many :pipelines, class_name: 'Ci::Pipeline' + has_many :clusters, class_name: 'Ci::Cluster' # Ci::Build objects store data on the file system such as artifact files and # build traces. Currently there's no efficient way of removing this data in diff --git a/app/views/projects/clusters/edit.html.haml b/app/views/projects/clusters/edit.html.haml new file mode 100644 index 00000000000..6445b3ee75d --- /dev/null +++ b/app/views/projects/clusters/edit.html.haml @@ -0,0 +1,2 @@ +edit/show cluster += @cluster.inspect diff --git a/app/views/projects/clusters/new.html.haml b/app/views/projects/clusters/new.html.haml new file mode 100644 index 00000000000..5e291d07835 --- /dev/null +++ b/app/views/projects/clusters/new.html.haml @@ -0,0 +1,17 @@ +Create a new cluster + +%br + +- if @authorize_url + I have not authenticated yet. I can authenticate from + = link_to("authenticate from here", @authorize_url) +- else + I have already authenticated. + %br + Avaiable GCP project lists + %br + Avaiable zones + %br + = link_to "Create on Google Container Engine", namespace_project_clusters_path(@project.namespace, @project, param1: 'value1', param2: 'value2'), method: :post + = link_to "Use existing kubernets cluster", namespace_project_clusters_path(@project.namespace, @project, param1: 'value1', param2: 'value2'), method: :post + diff --git a/config/routes.rb b/config/routes.rb index 5683725c8a2..405bfcc2d8e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -87,6 +87,7 @@ Rails.application.routes.draw do resources :issues, module: :boards, only: [:index, :update] end + draw :google_api draw :import draw :uploads draw :explore diff --git a/config/routes/google_api.rb b/config/routes/google_api.rb new file mode 100644 index 00000000000..57e15d0d39c --- /dev/null +++ b/config/routes/google_api.rb @@ -0,0 +1,5 @@ +namespace :google_api do + resource :authorizations, only: [], controller: :authorizations do + match :callback, via: [:get, :post] + end +end diff --git a/config/routes/project.rb b/config/routes/project.rb index b36d13888cd..5a43e2274a6 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -183,6 +183,13 @@ constraints(ProjectUrlConstrainer.new) do end end + resources :clusters, except: [:show, :destroy] do + collection do + get :gcp_projects # TODO: This doesn't belong here. Grape or under user. Hint. Serilizer + get :gke_zones + end + end + resources :environments, except: [:destroy] do member do post :stop diff --git a/db/migrate/20170924094327_create_ci_clusters.rb b/db/migrate/20170924094327_create_ci_clusters.rb new file mode 100644 index 00000000000..86e75edf203 --- /dev/null +++ b/db/migrate/20170924094327_create_ci_clusters.rb @@ -0,0 +1,29 @@ +class CreateCiClusters < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :ci_clusters do |t| + t.integer :project_id + t.integer :owner_id + t.datetime_with_timezone :created_at, null: false + t.datetime_with_timezone :updated_at, null: false + t.boolean :enabled, default: true + t.string :end_point + t.text :ca_cert # Base64? + t.string :token + t.string :username + t.string :password + t.string :project_namespace + t.integer :creation_type # manual or on_gke + end + + # TODO: fk, index, encypt + + add_foreign_key :ci_clusters, :projects + add_foreign_key :ci_clusters, :users, column: :owner_id + end + + def down + drop_table :ci_clusters + end +end diff --git a/db/schema.rb b/db/schema.rb index 80ef91ec95d..5258adcbcb7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170921115009) do +ActiveRecord::Schema.define(version: 20170924094327) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -32,8 +32,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.text "description", null: false t.string "header_logo" t.string "logo" - t.datetime_with_timezone "created_at", null: false - t.datetime_with_timezone "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.text "description_html" t.integer "cached_markdown_version" end @@ -101,10 +101,6 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.text "help_page_text_html" t.text "shared_runners_text_html" t.text "after_sign_up_text_html" - t.integer "rsa_key_restriction", default: 0, null: false - t.integer "dsa_key_restriction", default: 0, null: false - t.integer "ecdsa_key_restriction", default: 0, null: false - t.integer "ed25519_key_restriction", default: 0, null: false t.boolean "housekeeping_enabled", default: true, null: false t.boolean "housekeeping_bitmaps_enabled", default: true, null: false t.integer "housekeeping_incremental_repack_period", default: 10, null: false @@ -132,6 +128,10 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.boolean "password_authentication_enabled" t.integer "performance_bar_allowed_group_id" t.boolean "hashed_storage_enabled", default: false, null: false + t.integer "rsa_key_restriction", default: 0, null: false + t.integer "dsa_key_restriction", default: 0, null: false + t.integer "ecdsa_key_restriction", default: 0, null: false + t.integer "ed25519_key_restriction", default: 0, null: false t.boolean "project_export_enabled", default: true, null: false t.boolean "auto_devops_enabled", default: false, null: false end @@ -256,7 +256,6 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree - add_index "ci_builds", ["id"], name: "index_for_ci_builds_retried_migration", where: "(retried IS NULL)", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree @@ -267,6 +266,21 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree + create_table "ci_clusters", force: :cascade do |t| + t.integer "project_id" + t.integer "owner_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "enabled", default: true + t.string "end_point" + t.text "ca_cert" + t.string "token" + t.string "username" + t.string "password" + t.string "project_namespace" + t.integer "creation_type" + end + create_table "ci_group_variables", force: :cascade do |t| t.string "key", null: false t.text "value" @@ -275,8 +289,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.string "encrypted_value_iv" t.integer "group_id", null: false t.boolean "protected", default: false, null: false - t.datetime_with_timezone "created_at", null: false - t.datetime_with_timezone "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree @@ -288,8 +302,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.string "encrypted_value_salt" t.string "encrypted_value_iv" t.integer "pipeline_schedule_id", null: false - t.datetime_with_timezone "created_at" - t.datetime_with_timezone "updated_at" + t.datetime "created_at" + t.datetime "updated_at" end add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree @@ -341,12 +355,14 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.integer "auto_canceled_by_id" t.integer "pipeline_schedule_id" t.integer "source" - t.integer "config_source" t.boolean "protected" + t.integer "iid" + t.integer "config_source" end add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree add_index "ci_pipelines", ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree + add_index "ci_pipelines", ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, using: :btree add_index "ci_pipelines", ["project_id", "ref", "status"], name: "index_ci_pipelines_on_project_id_and_ref_and_status", using: :btree add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree add_index "ci_pipelines", ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree @@ -538,8 +554,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.integer "project_id" t.integer "author_id", null: false t.integer "target_id" - t.datetime_with_timezone "created_at", null: false - t.datetime_with_timezone "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "action", limit: 2, null: false t.string "target_type" end @@ -577,8 +593,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree create_table "gpg_keys", force: :cascade do |t| - t.datetime_with_timezone "created_at", null: false - t.datetime_with_timezone "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "user_id" t.binary "primary_keyid" t.binary "fingerprint" @@ -590,8 +606,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "gpg_keys", ["user_id"], name: "index_gpg_keys_on_user_id", using: :btree create_table "gpg_signatures", force: :cascade do |t| - t.datetime_with_timezone "created_at", null: false - t.datetime_with_timezone "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "project_id" t.integer "gpg_key_id" t.binary "commit_sha" @@ -789,8 +805,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree create_table "merge_request_diff_commits", id: false, force: :cascade do |t| - t.datetime_with_timezone "authored_date" - t.datetime_with_timezone "committed_date" + t.datetime "authored_date" + t.datetime "committed_date" t.integer "merge_request_diff_id", null: false t.integer "relative_order", null: false t.binary "sha", null: false @@ -1113,8 +1129,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do create_table "project_auto_devops", force: :cascade do |t| t.integer "project_id", null: false - t.datetime_with_timezone "created_at", null: false - t.datetime_with_timezone "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.boolean "enabled" t.string "domain" end @@ -1204,7 +1220,6 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.string "repository_storage", default: "default", null: false t.boolean "request_access_enabled", default: false, null: false t.boolean "has_external_wiki" - t.string "ci_config_path" t.boolean "lfs_enabled" t.text "description_html" t.boolean "only_allow_merge_if_all_discussions_are_resolved" @@ -1212,8 +1227,9 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.integer "auto_cancel_pending_pipelines", default: 1, null: false t.string "import_jid" t.integer "cached_markdown_version" - t.text "delete_error" t.datetime "last_repository_updated_at" + t.string "ci_config_path" + t.text "delete_error" t.integer "storage_version", limit: 2 t.boolean "resolve_outdated_diff_discussions" end @@ -1685,6 +1701,8 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify add_foreign_key "ci_builds", "ci_stages", column: "stage_id", name: "fk_3a9eaa254d", on_delete: :cascade add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade + add_foreign_key "ci_clusters", "projects" + add_foreign_key "ci_clusters", "users", column: "owner_id" add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade diff --git a/lib/google_api/authentication.rb b/lib/google_api/authentication.rb new file mode 100644 index 00000000000..d7b473525fb --- /dev/null +++ b/lib/google_api/authentication.rb @@ -0,0 +1,51 @@ +module GoogleApi + class Authentication + attr_reader :access_token, :redirect_uri, :state + + def initialize(access_token, redirect_uri, state: nil) + @access_token = access_token + @redirect_uri = redirect_uri + @state = state + end + + def client + return @client if defined?(@client) + + unless config + raise 'OAuth configuration for google_oauth2 missing.' + end + + @client = ::OAuth2::Client.new( + config.app_id, + config.app_secret, + site: 'https://accounts.google.com', + token_url: '/o/oauth2/token', + authorize_url: '/o/oauth2/auth' + ) + end + + def authorize_url + client.auth_code.authorize_url( + redirect_uri: redirect_uri, + scope: scope, + state: state # This is used for arbitary redirection + ) + end + + def get_token(code) + client.auth_code.get_token(code, redirect_uri: redirect_uri).token + end + + protected + + def scope + raise NotImplementedError + end + + private + + def config + Gitlab.config.omniauth.providers.find { |provider| provider.name == "google_oauth2" } + end + end +end diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb new file mode 100644 index 00000000000..2c2aefc542c --- /dev/null +++ b/lib/google_api/cloud_platform/client.rb @@ -0,0 +1,24 @@ +module GoogleApi + module CloudPlatform + class Client < GoogleApi::Authentication + # Google::Apis::ContainerV1::ContainerService.new + def scope + 'https://www.googleapis.com/auth/cloud-platform' + end + + def projects_zones_clusters_get + # TODO: + # service = Google::Apis::ContainerV1::ContainerService.new + # service.authorization = access_token + # project_id = params['project_id'] + # ... + # response = service.list_zone_clusters(project_id, zone) + response + end + + def projects_zones_clusters_create + # TODO + end + end + end +end |