Skip to content
Snippets Groups Projects
Commit 64be8d70 authored by Kamil Trzcińśki's avatar Kamil Trzcińśki
Browse files

Improve backend structure of data

parent 461c385e
No related branches found
No related tags found
No related merge requests found
Showing
with 129 additions and 76 deletions
class Projects::Clusters::ApplicationsController < Projects::ApplicationController
before_action :cluster
before_action :application_class, only: [:create]
before_action :authorize_read_cluster!
before_action :authorize_create_cluster!, only: [:create]
def new
end
def create
return render_404 if application
new_application = application_class.create(cluster: cluster)
respond_to do |format|
format.json do
if new_application.persisted?
head :ok
else
head :bad_request
end
end
end
end
private
def cluster
@cluster ||= project.clusters.find_by(cluster_id: params[:cluster_id]).present(current_user: current_user)
end
def application_class
Clusters::Cluster::Applications.find(params[:application])
end
def application
application_class.find_by(cluster: cluster)
end
end
module Clusters
module Kubernetes
class HelmApp < ActiveRecord::Base
module Applications
class Helm < ActiveRecord::Base
self.table_name = 'clusters_applications_helm'
NAME = 'helm'.freeze
 
include ::Clusters::Concerns::AppStatus
belongs_to :kubernetes_service, class_name: 'KubernetesService', foreign_key: :service_id
 
default_value_for :version, Gitlab::Clusters::Helm::HELM_VERSION
belongs_to :cluser, class_name: 'Clusters::Cluster', foreign_key: :cluster_id
 
alias_method :cluster, :kubernetes_service
default_value_for :version, Gitlab::Clusters::Helm::HELM_VERSION
 
def name
NAME
Loading
Loading
Loading
Loading
@@ -4,6 +4,10 @@ module Clusters
 
self.table_name = 'clusters'
 
APPLICATIONS = {
Clusters::Applications::Helm::NAME => Clusters::Applications::Helm
}
belongs_to :user
 
has_many :cluster_projects, class_name: 'Clusters::Project'
Loading
Loading
@@ -15,13 +19,14 @@ module Clusters
# We have to ":destroy" it today to ensure that we clean also the Kubernetes Integration
has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
 
has_one :application_helm, class_name: 'Clusters::Applications::Helm'
accepts_nested_attributes_for :provider_gcp, update_only: true
accepts_nested_attributes_for :platform_kubernetes, update_only: true
 
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
delegate :status_name, to: :provider, allow_nil: true
delegate :on_creation?, to: :provider, allow_nil: true
Loading
Loading
@@ -38,6 +43,14 @@ module Clusters
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
 
def status_name
if provider
provider.status_name
else
:created
end
end
def provider
return provider_gcp if gcp?
end
Loading
Loading
@@ -53,6 +66,10 @@ module Clusters
end
alias_method :project, :first_project
 
def kubeclient
platform_kubernetes.kubeclient if kubernetes?
end
private
 
def restrict_modification
Loading
Loading
module Clusters
module Kubernetes
def self.table_name_prefix
'clusters_kubernetes_'
end
def self.app(app_name)
case app_name
when HelmApp::NAME
HelmApp
else
raise ArgumentError, "Unknown app #{app_name}"
end
end
end
end
Loading
Loading
@@ -60,6 +60,10 @@ module Clusters
self.class.namespace_for_project(project) if project
end
 
def kubeclient
@kubeclient ||= kubernetes_service.kubeclient if manages_kubernetes_service?
end
private
 
def enforce_namespace_to_lower_case
Loading
Loading
Loading
Loading
@@ -189,6 +189,7 @@ class Project < ActiveRecord::Base
 
has_one :cluster_project, class_name: 'Clusters::Project'
has_one :cluster, through: :cluster_project, class_name: 'Clusters::Cluster'
has_many :clusters, 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
Loading
Loading
Loading
Loading
@@ -3,8 +3,6 @@ class KubernetesService < DeploymentService
include Gitlab::Kubernetes
include ReactiveCaching
 
has_one :helm_app, class_name: 'Clusters::Kubernetes::HelmApp', foreign_key: :service_id
self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] }
 
# Namespace defaults to the project path, but can be overridden in case that
Loading
Loading
@@ -138,8 +136,8 @@ class KubernetesService < DeploymentService
{ pods: read_pods }
end
 
def helm
Gitlab::Clusters::Helm.new(build_kubeclient!)
def kubeclient
@kubeclient ||= build_kubeclient!
end
 
TEMPLATE_PLACEHOLDER = 'Kubernetes namespace'.freeze
Loading
Loading
Loading
Loading
@@ -5,5 +5,11 @@ module Clusters
def gke_cluster_url
"https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp?
end
def applications
Clusters::Cluster::APPLICATIONS.map do |key, value|
value.find_by(cluster_id: id)
end.compact
end
end
end
class ClusterAppEntity < Grape::Entity
expose :name
expose :status_name, as: :status
expose :status_reason
end
Loading
Loading
@@ -3,16 +3,5 @@ class ClusterEntity < Grape::Entity
 
expose :status_name, as: :status
expose :status_reason
expose :applications do |cluster, options|
if cluster.created?
{
helm: { status: 'installed' },
ingress: { status: 'error', status_reason: 'Missing namespace' },
runner: { status: 'installing' },
prometheus: { status: 'installable' }
}
else
{}
end
end
expose :applications, using: ClusterAppEntity
end
Loading
Loading
@@ -8,10 +8,16 @@ module Clusters
 
protected
 
def helm
return @helm if defined?(@helm)
def cluster
app.cluster
end
def kubeclient
cluster.kubeclient
end
 
@helm = @app.cluster.helm
def helm_api
@helm ||= Gitlab::Clusters::Helm.new(kubeclient)
end
end
end
Loading
Loading
@@ -3,8 +3,8 @@ module Clusters
def execute
return unless app.installing?
 
phase = helm.installation_status(app)
log = helm.installation_log(app) if phase == 'Failed'
phase = helm_api.installation_status(app)
log = helm_api.installation_log(app) if phase == 'Failed'
yield(phase, log) if block_given?
rescue KubeException => ke
app.make_errored!("Kubernetes error: #{ke.message}") unless app.errored?
Loading
Loading
module Clusters
class FinalizeAppInstallationService < BaseHelmService
def execute
helm.delete_installation_pod!(app)
helm_api.delete_installation_pod!(app)
 
app.make_errored!('Installation aborted') if aborted?
end
Loading
Loading
Loading
Loading
@@ -4,14 +4,14 @@ module Clusters
return unless app.scheduled?
 
begin
helm.install(app)
helm_api.install(app)
if app.make_installing
ClusterWaitForAppInstallationWorker.perform_in(
ClusterWaitForAppInstallationWorker::INITIAL_INTERVAL, app.name, app.id)
else
app.make_errored!("Failed to update app record; #{app.errors}")
end
rescue KubeException => ke
app.make_errored!("Kubernetes error: #{ke.message}")
rescue StandardError => e
Loading
Loading
Loading
Loading
@@ -3,8 +3,9 @@ module ClusterApp
 
included do
def find_app(app_name, id)
app = Clusters::Kubernetes.app(app_name).find(id)
yield(app) if block_given?
Clusters::Applications.const_get(app_name.classify).find(id).try do |app|
yield(app) if block_given?
end
end
end
end
Loading
Loading
@@ -190,6 +190,10 @@ constraints(ProjectUrlConstrainer.new) do
 
member do
get :status, format: :json
scope '*application' do
resource :applications, only: [:create]
end
end
end
 
Loading
Loading
class CreateClustersKubernetesHelmApps < ActiveRecord::Migration
def change
create_table :clusters_kubernetes_helm_apps do |t|
t.integer :status, null: false
create_table :clusters_applications_helm do |t|
t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade }
 
t.datetime_with_timezone :created_at, null: false
t.datetime_with_timezone :updated_at, null: false
t.references :service, index: true, null: false, foreign_key: { on_delete: :cascade }
t.integer :status, null: false
t.string :version, null: false
t.text :status_reason
end
Loading
Loading
Loading
Loading
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
 
ActiveRecord::Schema.define(version: 20171017145932) do
ActiveRecord::Schema.define(version: 20171031100710) do
 
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Loading
Loading
@@ -464,9 +464,7 @@ ActiveRecord::Schema.define(version: 20171017145932) do
 
create_table "cluster_platforms_kubernetes", force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "api_url"
t.string "api_url"
t.text "ca_cert"
t.string "namespace"
t.string "username"
Loading
Loading
@@ -474,6 +472,8 @@ ActiveRecord::Schema.define(version: 20171017145932) do
t.string "encrypted_password_iv"
t.text "encrypted_token"
t.string "encrypted_token_iv"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
end
 
add_index "cluster_platforms_kubernetes", ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree
Loading
Loading
@@ -491,33 +491,39 @@ ActiveRecord::Schema.define(version: 20171017145932) do
create_table "cluster_providers_gcp", force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status"
t.integer "num_nodes", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
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_with_timezone "created_at", null: false
t.datetime_with_timezone "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", null: false
t.integer "provider_type"
t.integer "platform_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.boolean "enabled", default: true
t.string "name", null: false
t.integer "provider_type"
t.integer "platform_type"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
end
 
add_index "clusters", ["enabled"], name: "index_clusters_on_enabled", using: :btree
add_index "clusters", ["user_id"], name: "index_clusters_on_user_id", using: :btree
create_table "clusters_applications_helm", force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "status", null: false
t.string "version", null: false
t.text "status_reason"
end
 
create_table "container_repositories", force: :cascade do |t|
t.integer "project_id", null: false
Loading
Loading
@@ -1872,6 +1878,7 @@ ActiveRecord::Schema.define(version: 20171017145932) do
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 "clusters_applications_helm", "clusters", on_delete: :cascade
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
Loading
Loading
require 'rails_helper'
require_relative '../kubernetes_spec'
 
RSpec.describe Clusters::Kubernetes::HelmApp, type: :model do
RSpec.describe Clusters::Applications::Helm, type: :model do
it_behaves_like 'a registered kubernetes app'
 
it { is_expected.to belong_to(:kubernetes_service) }
Loading
Loading
require 'rails_helper'
RSpec.shared_examples 'a registered kubernetes app' do
let(:name) { described_class::NAME }
it 'can be retrieved with Clusters::Kubernetes.app' do
expect(Clusters::Kubernetes.app(name)).to eq(described_class)
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment