Skip to content
Snippets Groups Projects
Commit 3db5b703 authored by Fatih Acet's avatar Fatih Acet Committed by Nick Thomas
Browse files

Add terminal UI and controller actions

parent c3d972f4
No related branches found
No related tags found
No related merge requests found
Showing
with 189 additions and 13 deletions
Loading
Loading
@@ -76,6 +76,7 @@
helpPagePath: environmentsData.helpPagePath,
commitIconSvg: environmentsData.commitIconSvg,
playIconSvg: environmentsData.playIconSvg,
terminalIconSvg: environmentsData.terminalIconSvg,
};
},
 
Loading
Loading
@@ -230,6 +231,7 @@
:can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed"
:play-icon-svg="playIconSvg"
:terminal-icon-svg="terminalIconSvg"
:commit-icon-svg="commitIconSvg"></tr>
 
<tr v-if="model.isOpen && model.children && model.children.length > 0"
Loading
Loading
@@ -240,6 +242,7 @@
:can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed"
:play-icon-svg="playIconSvg"
:terminal-icon-svg="terminalIconSvg"
:commit-icon-svg="commitIconSvg">
</tr>
 
Loading
Loading
Loading
Loading
@@ -8,6 +8,7 @@
/*= require ./environment_external_url */
/*= require ./environment_stop */
/*= require ./environment_rollback */
/*= require ./environment_terminal_button */
 
(() => {
/**
Loading
Loading
@@ -33,6 +34,7 @@
'external-url-component': window.gl.environmentsList.ExternalUrlComponent,
'stop-component': window.gl.environmentsList.StopComponent,
'rollback-component': window.gl.environmentsList.RollbackComponent,
'terminal-button-component': window.gl.environmentsList.TerminalButtonComponent,
},
 
props: {
Loading
Loading
@@ -68,6 +70,12 @@
type: String,
required: false,
},
terminalIconSvg: {
type: String,
required: false,
},
},
 
data() {
Loading
Loading
@@ -506,6 +514,14 @@
</stop-component>
</div>
 
<div v-if="model.terminal_path"
class="inline js-terminal-button-container">
<terminal-button-component
:terminal-icon-svg="terminalIconSvg"
:terminal-path="model.terminal_path">
</terminal-button-component>
</div>
<div v-if="canRetry && canCreateDeployment"
class="inline js-rollback-component-container">
<rollback-component
Loading
Loading
/*= require vue */
/* global Vue */
(() => {
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};
window.gl.environmentsList.TerminalButtonComponent = Vue.component('terminal-button-component', {
props: {
terminalPath: {
type: String,
default: '',
},
terminalIconSvg: {
type: String,
default: '',
},
},
template: `
<a class="btn terminal-button"
:href="terminalPath">
<span class="js-terminal-icon-container" v-html="terminalIconSvg"></span>
</a>
`,
});
})();
Loading
Loading
@@ -230,6 +230,13 @@
}
}
 
.btn-terminal {
svg {
height: 14px;
width: 18px;
}
}
.btn-lg {
padding: 12px 20px;
}
Loading
Loading
Loading
Loading
@@ -734,3 +734,23 @@
padding: 5px 5px 5px 7px;
}
}
.terminal-icon {
margin-left: 3px;
}
.terminal-container {
.content-block {
border-bottom: none;
}
#terminal {
margin-top: 10px;
min-height: 450px;
box-sizing: border-box;
> div {
min-height: 450px;
}
}
}
Loading
Loading
@@ -4,7 +4,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_create_deployment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update]
before_action :environment, only: [:show, :edit, :update, :stop]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize]
before_action :verify_api_request!, only: :terminal_websocket_authorize
 
def index
@scope = params[:scope]
Loading
Loading
@@ -14,7 +16,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
format.html
format.json do
render json: EnvironmentSerializer
.new(project: @project)
.new(project: @project, user: current_user)
.represent(@environments)
end
end
Loading
Loading
@@ -65,11 +67,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController
 
# GET .../terminal.ws : implemented in gitlab-workhorse
def terminal_websocket_authorize
Gitlab::Workhorse.verify_api_request!(request.headers)
# Just return the first terminal for now. If the list is in the process of
# being looked up, this may result in a 404 response, so the frontend
# should retry
# should retry those errors
terminal = environment.terminals.try(:first)
if terminal
set_workhorse_internal_api_content_type
Loading
Loading
@@ -81,6 +81,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
 
private
 
def verify_api_request!
Gitlab::Workhorse.verify_api_request!(request.headers)
end
def environment_params
params.require(:environment).permit(:name, :external_url)
end
Loading
Loading
Loading
Loading
@@ -8,7 +8,6 @@ class EnvironmentEntity < Grape::Entity
expose :environment_type
expose :last_deployment, using: DeploymentEntity
expose :stoppable?
expose :has_terminals?, as: :has_terminals
 
expose :environment_path do |environment|
namespace_project_environment_path(
Loading
Loading
@@ -25,10 +24,11 @@ class EnvironmentEntity < Grape::Entity
end
 
expose :terminal_path, if: ->(environment, _) { environment.has_terminals? } do |environment|
terminal_namespace_project_environment_path(
environment.project.namespace,
environment.project,
environment)
can?(request.user, :admin_environment, environment.project) &&
terminal_namespace_project_environment_path(
environment.project.namespace,
environment.project,
environment)
end
 
expose :created_at, :updated_at
Loading
Loading
Loading
Loading
@@ -8,4 +8,8 @@ module RequestAwareEntity
def request
@options.fetch(:request)
end
def can?(object, action, subject)
Ability.allowed?(object, action, subject)
end
end
- if environment.has_terminals? && can?(current_user, :admin_environment, @project)
= link_to terminal_namespace_project_environment_path(@project.namespace, @project, environment), class: 'btn terminal-button' do
= icon('terminal')
Loading
Loading
@@ -15,4 +15,5 @@
"help-page-path" => help_page_path("ci/environments"),
"css-class" => container_class,
"commit-icon-svg" => custom_icon("icon_commit"),
"terminal-icon-svg" => custom_icon("icon_terminal"),
"play-icon-svg" => custom_icon("icon_play")}}
Loading
Loading
@@ -8,6 +8,7 @@
%h3.page-title= @environment.name.capitalize
.col-md-3
.nav-controls
= render 'projects/environments/terminal_button', environment: @environment
= render 'projects/environments/external_url', environment: @environment
- if can?(current_user, :update_environment, @environment)
= link_to 'Edit', edit_namespace_project_environment_path(@project.namespace, @project, @environment), class: 'btn'
Loading
Loading
- @no_container = true
- page_title "Terminal for environment", @environment.name
= render "projects/pipelines/head"
- content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm/xterm"
= page_specific_javascript_tag("terminal/terminal_bundle.js")
%div{class: container_class}
.top-area
.row
.col-sm-6
%h3.page-title
Terminal for environment
= @environment.name
.col-sm-6
.nav-controls
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.terminal-container{class: container_class}
#terminal{data:{project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws"}}
<svg width="19" height="14" viewBox="0 0 19 14" xmlns="http://www.w3.org/2000/svg"><rect fill="#848484" x="7.2" y="9.25" width="6.46" height="1.5" rx=".5"/><path d="M5.851 7.016L3.81 9.103a.503.503 0 0 0 .017.709l.35.334c.207.198.524.191.717-.006l2.687-2.748a.493.493 0 0 0 .137-.376.493.493 0 0 0-.137-.376L4.894 3.892a.507.507 0 0 0-.717-.006l-.35.334a.503.503 0 0 0-.017.709L5.85 7.016z"/><path d="M1.25 11.497c0 .691.562 1.253 1.253 1.253h13.994c.694 0 1.253-.56 1.253-1.253V2.503c0-.691-.562-1.253-1.253-1.253H2.503c-.694 0-1.253.56-1.253 1.253v8.994zM2.503 0h13.994A2.504 2.504 0 0 1 19 2.503v8.994A2.501 2.501 0 0 1 16.497 14H2.503A2.504 2.504 0 0 1 0 11.497V2.503A2.501 2.501 0 0 1 2.503 0z"/></svg>
Loading
Loading
@@ -148,6 +148,8 @@ constraints(ProjectUrlConstrainer.new) do
resources :environments, except: [:destroy] do
member do
post :stop
get :terminal
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
end
end
 
Loading
Loading
Loading
Loading
@@ -123,6 +123,7 @@ describe Projects::EnvironmentsController do
context 'and invalid id' do
it 'returns 404' do
get :terminal_websocket_authorize, environment_params(id: 666)
expect(response).to have_http_status(404)
end
end
Loading
Loading
Loading
Loading
@@ -42,6 +42,12 @@ FactoryGirl.define do
end
end
 
trait :test_repo do
after :create do |project|
TestEnv.copy_repo(project)
end
end
# Nest Project Feature attributes
transient do
wiki_access_level ProjectFeature::ENABLED
Loading
Loading
@@ -91,9 +97,7 @@ FactoryGirl.define do
factory :project, parent: :empty_project do
path { 'gitlabhq' }
 
after :create do |project|
TestEnv.copy_repo(project)
end
test_repo
end
 
factory :forked_project_with_submodules, parent: :empty_project do
Loading
Loading
Loading
Loading
@@ -38,6 +38,10 @@ feature 'Environment', :feature do
scenario 'does not show a re-deploy button for deployment without build' do
expect(page).not_to have_link('Re-deploy')
end
scenario 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
end
 
context 'with related deployable present' do
Loading
Loading
@@ -60,6 +64,10 @@ feature 'Environment', :feature do
expect(page).not_to have_link('Stop')
end
 
scenario 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
context 'with manual action' do
given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') }
 
Loading
Loading
@@ -84,6 +92,26 @@ feature 'Environment', :feature do
end
end
 
context 'with terminal' do
let(:project) { create(:kubernetes_project, :test_repo) }
context 'for project master' do
let(:role) { :master }
scenario 'it shows the terminal button' do
expect(page).to have_terminal_button
end
end
context 'for developer' do
let(:role) { :developer }
scenario 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
end
end
context 'with stop action' do
given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') }
given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') }
Loading
Loading
@@ -158,4 +186,8 @@ feature 'Environment', :feature do
environment.project,
environment)
end
def have_terminal_button
have_link(nil, href: terminal_namespace_project_environment_path(project.namespace, project, environment))
end
end
Loading
Loading
@@ -113,6 +113,10 @@ feature 'Environments page', :feature, :js do
expect(page).not_to have_css('external-url')
end
 
scenario 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
context 'with external_url' do
given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') }
given(:build) { create(:ci_build, pipeline: pipeline) }
Loading
Loading
@@ -145,6 +149,26 @@ feature 'Environments page', :feature, :js do
end
end
end
context 'with terminal' do
let(:project) { create(:kubernetes_project, :test_repo) }
context 'for project master' do
let(:role) { :master }
scenario 'it shows the terminal button' do
expect(page).to have_terminal_button
end
end
context 'for developer' do
let(:role) { :developer }
scenario 'does not show terminal button' do
expect(page).not_to have_terminal_button
end
end
end
end
end
end
Loading
Loading
@@ -195,6 +219,10 @@ feature 'Environments page', :feature, :js do
end
end
 
def have_terminal_button
have_link(nil, href: terminal_namespace_project_environment_path(project.namespace, project, environment))
end
def visit_environments(project)
visit namespace_project_environments_path(project.namespace, project)
end
Loading
Loading
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