diff options
author | Kamil Trzciński <ayufan@ayufan.eu> | 2018-09-12 20:26:19 +0200 |
---|---|---|
committer | Kamil Trzciński <ayufan@ayufan.eu> | 2018-09-12 20:26:19 +0200 |
commit | ca2046167da10e583d27dd5d94ff6930fb44be5f (patch) | |
tree | 2a4a0c29ee5fba1a8d72127eaeebcd07c3d11b7e | |
parent | 88225ea3085d01f083a0ce693c4b87a7f9514f8d (diff) | |
download | gitlab-ce-knative-first-class-support.tar.gz |
Add functions runtimeknative-first-class-support
-rw-r--r-- | app/controllers/projects/knative_controller.rb | 61 | ||||
-rw-r--r-- | app/models/project.rb | 2 | ||||
-rw-r--r-- | app/models/serverless/functions.rb | 101 | ||||
-rw-r--r-- | app/views/layouts/nav/sidebar/_project.html.haml | 4 | ||||
-rw-r--r-- | app/views/projects/knative/_form.html.haml | 26 | ||||
-rw-r--r-- | app/views/projects/knative/_functions.html.haml | 13 | ||||
-rw-r--r-- | app/views/projects/knative/_triggers.html.haml (renamed from app/views/projects/knative/_list.html.haml) | 25 | ||||
-rw-r--r-- | app/views/projects/knative/_use.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/knative/edit.html.haml | 11 | ||||
-rw-r--r-- | app/views/projects/knative/index.html.haml | 12 | ||||
-rw-r--r-- | app/views/projects/knative/new.html.haml | 12 | ||||
-rw-r--r-- | db/migrate/20180912161550_add_serverless_functions.rb | 42 |
12 files changed, 287 insertions, 24 deletions
diff --git a/app/controllers/projects/knative_controller.rb b/app/controllers/projects/knative_controller.rb index 3785788c0a8..f1f1bb84f75 100644 --- a/app/controllers/projects/knative_controller.rb +++ b/app/controllers/projects/knative_controller.rb @@ -1,6 +1,67 @@ class Projects::KnativeController < Projects::ApplicationController respond_to :html + before_action :authorize_read_cluster! + before_action :serverless_function, only: [:edit, :update, :destroy] + + def index + end + def index + @serverless_functions = project.serverless_functions + end + + def new + @serverless_function = project.serverless_functions.new + end + + def create + @serverless_function = project.serverless_functions.create(create_params) + + if @serverless_function.persisted? + flash[:notice] = 'Function was successfully created.' + redirect_to project_knative_index_path(@project) + else + render :new + end + end + + def edit end + + def update + if serverless_function.update(update_params) + flash[:notice] = 'Function was successfully updated.' + redirect_to project_knative_index_path(@project) + else + render :edit + end + end + + def destroy + if serverless_function.destroy + flash[:notice] = 'Function was successfully updated.' + redirect_to project_knative_index_path(@project) + else + flash[:notice] = 'Function was not removed.' + redirect_to project_knative_index_path(@project) + end + end + + protected + + def serverless_function + @serverless_function ||= project.serverless_functions.find(params[:id]) + end + + def create_params + params.require(:serverless_functions) + .permit(:name, :function_code, :runtime_image, :active) + end + + def update_params + params.require(:serverless_functions) + .permit(:name, :function_code, :runtime_image) + end + end diff --git a/app/models/project.rb b/app/models/project.rb index 8928bffd36c..8e091c31712 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -268,6 +268,8 @@ class Project < ActiveRecord::Base has_many :remote_mirrors, inverse_of: :project + has_many :serverless_functions, class_name: 'Serverless::Functions' + accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :project_feature, update_only: true accepts_nested_attributes_for :import_data diff --git a/app/models/serverless/functions.rb b/app/models/serverless/functions.rb new file mode 100644 index 00000000000..2c9d7ac52aa --- /dev/null +++ b/app/models/serverless/functions.rb @@ -0,0 +1,101 @@ +module Serverless + class Functions < ActiveRecord::Base + self.table_name = 'serverless_functions' + + belongs_to :project + + validates :project, presence: true + validates :runtime_image, + presence: true, + length: 2..255 + + validates :function_code, + presence: true + + after_save :create_or_update_function + after_destroy :destroy_function + + private + + def knative_client + @knative_client ||= project.clusters&.first&.application_knative&.client + end + + def create_or_update_function + raise ArgumentError, "knative is not installed" unless knative_client + + @existing_service = knative_client.get_service(name, function_namespace) + + knative_client.update_service(create_metadata) + rescue ::Kubeclient::HttpError => e + raise e unless e.error_code == 404 + + knative_client.create_service(create_metadata) + end + + def destroy_function + return unless knative_client + + knative_client.delete_service(name, function_namespace) + rescue ::Kubeclient::HttpError => e + raise e unless e.error_code == 404 + + false + end + + def update_metadata + ::Kubeclient::Resource.new.tap do |r| + r.metadata = { + #labels: { project_id: project_id.to_s, function_id: id.to_s } + } + r.spec = { + generation: (@existing_service&.spec || {})[:generation], + runLatest: { + configuration: { + revisionTemplate: { + spec: { + container: { + image: runtime_image, + env: [ + { name: 'FUNCTION', value: function_code.to_s } + ] + } + } + } + } + } + } + end + end + + def create_metadata + update_metadata.tap do |r| + r.apiVersion = 'serving.knative.dev/v1alpha1' + r.kind = 'Service' + r.metadata[:name] = name + r.metadata[:namespace] = function_namespace + r.metadata[:resourceVersion] = (@existing_service&.metadata || {})[:resourceVersion] + end + end + + def function_namespace + 'default' + end + + # apiVersion: serving.knative.dev/v1alpha1 # Current version of Knative + # kind: Service + # metadata: + # name: helloworld-go # The name of the app + # namespace: default # The namespace the app will use + # spec: + # runLatest: + # configuration: + # revisionTemplate: + # spec: + # container: + # image: gcr.io/knative-samples/helloworld-go # The URL to the image of the app + # env: + # - name: TARGET # The environment variable printed out by the sample app + # value: "Go Sample v1" + end +end diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 8173225f34d..531682724e5 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -217,9 +217,9 @@ - if project_nav_tab? :knative = nav_link(controller: :knative) do - = link_to project_knative_index_path(@project), title: _('Knative'), class: 'shortcuts-metrics' do + = link_to project_knative_index_path(@project), title: _('Functions'), class: 'shortcuts-metrics' do %span - = _('Knative') + = _('Functions') = nav_link(controller: :environments, action: [:index, :folder, :show, :new, :edit, :create, :update, :stop, :terminal]) do = link_to project_environments_path(@project), title: _('Environments'), class: 'shortcuts-environments' do diff --git a/app/views/projects/knative/_form.html.haml b/app/views/projects/knative/_form.html.haml new file mode 100644 index 00000000000..a97fb78028b --- /dev/null +++ b/app/views/projects/knative/_form.html.haml @@ -0,0 +1,26 @@ +- if @serverless_function.errors.any? + #error_explanation + .alert.alert-danger + - @serverless_function.errors.full_messages.each do |msg| + %p= msg + +.form-group.row + = f.label :name, class: 'col-form-label col-sm-2' do + Name + .col-sm-10 + = f.text_field :name, required: true, autocomplete: 'off', class: 'form-control' + +.form-group.row + = f.label :runtime_image, class: 'col-form-label col-sm-2' do + Runtime + .col-sm-10 + - options = ["registry.gitlab.com/ayufan/serverless-functions/functions/go:1.11.0", "gcr.io/knative-samples/helloworld-go"] + = f.select :runtime_image, options, multiple: false, class: 'form-control' + // = f.text_field :runtime_image, required: true, autocomplete: 'off', class: 'form-control' + +.form-group.row + = f.label :runtime, class: 'col-form-label col-sm-2' do + Code + .col-sm-10 + = f.text_area :function_code, rows: 5, class: 'form-control' + %span.help-inline Write a function code diff --git a/app/views/projects/knative/_functions.html.haml b/app/views/projects/knative/_functions.html.haml new file mode 100644 index 00000000000..d4f405b7d86 --- /dev/null +++ b/app/views/projects/knative/_functions.html.haml @@ -0,0 +1,13 @@ +.card + .card-header + Functions (#{@serverless_functions.count}) + + %ul.content-list.pages-domain-list + - @serverless_functions.each do |serverless_function| + %li.pages-domain-list-item.unstyled + = serverless_function.name + + %div.controls.d-none.d-md-block + = link_to 'Edit', edit_project_knative_path(@project, serverless_function), class: "btn btn-sm btn-grouped" + = link_to 'Remove', project_knative_path(@project, serverless_function), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" + diff --git a/app/views/projects/knative/_list.html.haml b/app/views/projects/knative/_triggers.html.haml index 785f053bcbd..082701d10a0 100644 --- a/app/views/projects/knative/_list.html.haml +++ b/app/views/projects/knative/_triggers.html.haml @@ -2,27 +2,20 @@ - services = cluster_client.get_services .card .card-header - Knative Services (#{services.count}) + Services (#{services.count}) + %ul.content-list.pages-domain-list - services.each do |service| %li.pages-domain-list-item.unstyled = service.metadata.name - %p - = service.status.domain - - - if traffic = service.status&.trafic&.first - %p - Traffic configuration: - = traffic.configurationName - Percent: - = traffic.percent - Revision: - = traffic.revisionName - %div.controls.d-none.d-md-block - %p - - if service.status.conditions.all? { |condition| condition.status } + - if service.status.conditions.all? { |condition| condition.status == "True" } %span.badge.badge-success Ready - else - %span.badge.badge-danger NotReady + %span.badge.badge-danger Not ready + + %p + %span.badge.badge-primary + = service.status.domain +
\ No newline at end of file diff --git a/app/views/projects/knative/_use.html.haml b/app/views/projects/knative/_use.html.haml index 73d1319511d..867307f6fba 100644 --- a/app/views/projects/knative/_use.html.haml +++ b/app/views/projects/knative/_use.html.haml @@ -3,7 +3,7 @@ Configure Knative... .card-body %p - Learn how to enable knative functions. + Learn how to enable use Knative Triggers functions. %ol %li diff --git a/app/views/projects/knative/edit.html.haml b/app/views/projects/knative/edit.html.haml new file mode 100644 index 00000000000..2c0df47054e --- /dev/null +++ b/app/views/projects/knative/edit.html.haml @@ -0,0 +1,11 @@ +- add_to_breadcrumbs "Functions", project_knative_index_path(@project) +- breadcrumb_title @serverless_function.name +- page_title @serverless_function.name +%h3.page-title + = @serverless_function.name +%hr.clearfix +%div + = form_for [@project, @serverless_function], url: project_knative_path(@project, @serverless_function), html: { class: 'fieldset-form' } do |f| + = render 'form', { f: f } + .form-actions + = f.submit 'Save Changes', class: "btn btn-save" diff --git a/app/views/projects/knative/index.html.haml b/app/views/projects/knative/index.html.haml index 4cd94ab1061..d1361dd6040 100644 --- a/app/views/projects/knative/index.html.haml +++ b/app/views/projects/knative/index.html.haml @@ -3,12 +3,14 @@ %h3.page-title.with-button Knative -%p.light - With GitLab Knative + - if can?(current_user, :create_cluster, @project) + = link_to new_project_knative_path(@project), class: 'btn btn-new float-right', title: 'New Function' do + New Function %hr.clearfix += render 'functions' + - if cluster_client = @project.clusters.first.application_knative.client - = render 'list', locals: { cluster_client: cluster_client } -- else - = render 'use' + = render 'triggers', locals: { cluster_client: cluster_client } + diff --git a/app/views/projects/knative/new.html.haml b/app/views/projects/knative/new.html.haml new file mode 100644 index 00000000000..a42479c4951 --- /dev/null +++ b/app/views/projects/knative/new.html.haml @@ -0,0 +1,12 @@ +- add_to_breadcrumbs "Functions", project_knative_index_path(@project) +- page_title 'New Function' +%h3.page-title + New Function +%hr.clearfix +%div + = form_for [@project, @serverless_function], url: project_knative_index_path(@project), html: { class: 'fieldset-form' } do |f| + = render 'form', { f: f } + .form-actions + = f.submit 'Create New Function', class: "btn btn-save" + .float-right + = link_to _('Cancel'), project_knative_index_path(@project), class: 'btn btn-cancel' diff --git a/db/migrate/20180912161550_add_serverless_functions.rb b/db/migrate/20180912161550_add_serverless_functions.rb new file mode 100644 index 00000000000..8514a7c7f0c --- /dev/null +++ b/db/migrate/20180912161550_add_serverless_functions.rb @@ -0,0 +1,42 @@ +# 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 AddServerlessFunctions < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index", "remove_concurrent_index" or + # "add_column_with_default" you must disable the use of transactions + # as these methods can not run in an existing transaction. + # When using "add_concurrent_index" or "remove_concurrent_index" methods make sure + # that either of them is the _only_ method called in the migration, + # any other changes should go in a separate migration. + # This ensures that upon failure _only_ the index creation or removing fails + # and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + create_table "serverless_functions" do |t| + t.integer "project_id", null: false, index: true + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false + t.string "name" + t.string "runtime_image" + t.text "function_code" + end + + add_index :serverless_functions, [:project_id, :name], unique: true + end +end |