diff options
Diffstat (limited to 'app')
48 files changed, 362 insertions, 139 deletions
diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js index 062577af385..06ef86ecb77 100644 --- a/app/assets/javascripts/blob/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq_viewer.js @@ -7,7 +7,7 @@ function onError() { return flash; } -function loadBalsamiqFile() { +export default function loadBalsamiqFile() { const viewer = document.getElementById('js-balsamiq-viewer'); if (!(viewer instanceof Element)) return; @@ -17,5 +17,3 @@ function loadBalsamiqFile() { const balsamiqViewer = new BalsamiqViewer(viewer); balsamiqViewer.loadFile(endpoint).catch(onError); } - -$(loadBalsamiqFile); diff --git a/app/assets/javascripts/blob/notebook_viewer.js b/app/assets/javascripts/blob/notebook_viewer.js index b7a0a195a92..226ae69893e 100644 --- a/app/assets/javascripts/blob/notebook_viewer.js +++ b/app/assets/javascripts/blob/notebook_viewer.js @@ -1,3 +1,3 @@ import renderNotebook from './notebook'; -document.addEventListener('DOMContentLoaded', renderNotebook); +export default renderNotebook; diff --git a/app/assets/javascripts/blob/pdf_viewer.js b/app/assets/javascripts/blob/pdf_viewer.js index 91abe9dd699..cabbb396ea7 100644 --- a/app/assets/javascripts/blob/pdf_viewer.js +++ b/app/assets/javascripts/blob/pdf_viewer.js @@ -1,3 +1,3 @@ import renderPDF from './pdf'; -document.addEventListener('DOMContentLoaded', renderPDF); +export default renderPDF; diff --git a/app/assets/javascripts/blob/sketch_viewer.js b/app/assets/javascripts/blob/sketch_viewer.js index 0640dd26855..2c1c6339fdb 100644 --- a/app/assets/javascripts/blob/sketch_viewer.js +++ b/app/assets/javascripts/blob/sketch_viewer.js @@ -1,8 +1,8 @@ /* eslint-disable no-new */ import SketchLoader from './sketch'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { const el = document.getElementById('js-sketch-viewer'); new SketchLoader(el); -}); +}; diff --git a/app/assets/javascripts/blob/stl_viewer.js b/app/assets/javascripts/blob/stl_viewer.js index f611c4fe640..63236b6477f 100644 --- a/app/assets/javascripts/blob/stl_viewer.js +++ b/app/assets/javascripts/blob/stl_viewer.js @@ -1,6 +1,6 @@ import Renderer from './3d_viewer'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { const viewer = new Renderer(document.getElementById('js-stl-viewer')); [].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => { @@ -16,4 +16,4 @@ document.addEventListener('DOMContentLoaded', () => { viewer.changeObjectMaterials(target.dataset.type); }); }); -}); +}; diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js index 612f604e725..92ea91c45a8 100644 --- a/app/assets/javascripts/blob/viewer/index.js +++ b/app/assets/javascripts/blob/viewer/index.js @@ -5,6 +5,7 @@ import axios from '../../lib/utils/axios_utils'; export default class BlobViewer { constructor() { BlobViewer.initAuxiliaryViewer(); + BlobViewer.initRichViewer(); this.initMainViewers(); } @@ -16,6 +17,38 @@ export default class BlobViewer { BlobViewer.loadViewer(auxiliaryViewer); } + static initRichViewer() { + const viewer = document.querySelector('.blob-viewer[data-type="rich"]'); + if (!viewer || !viewer.dataset.richType) return; + + const initViewer = promise => promise + .then(module => module.default(viewer)) + .catch((error) => { + Flash('Error loading file viewer.'); + throw error; + }); + + switch (viewer.dataset.richType) { + case 'balsamiq': + initViewer(import(/* webpackChunkName: 'balsamiq_viewer' */ '../balsamiq_viewer')); + break; + case 'notebook': + initViewer(import(/* webpackChunkName: 'notebook_viewer' */ '../notebook_viewer')); + break; + case 'pdf': + initViewer(import(/* webpackChunkName: 'pdf_viewer' */ '../pdf_viewer')); + break; + case 'sketch': + initViewer(import(/* webpackChunkName: 'sketch_viewer' */ '../sketch_viewer')); + break; + case 'stl': + initViewer(import(/* webpackChunkName: 'stl_viewer' */ '../stl_viewer')); + break; + default: + break; + } + } + initMainViewers() { this.$fileHolder = $('.file-holder'); if (!this.$fileHolder.length) return; diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index b070a59cf15..01aec4f36af 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -37,10 +37,11 @@ export default class Clusters { clusterStatusReason, helpPath, ingressHelpPath, + ingressDnsHelpPath, } = document.querySelector('.js-edit-cluster-form').dataset; this.store = new ClustersStore(); - this.store.setHelpPaths(helpPath, ingressHelpPath); + this.store.setHelpPaths(helpPath, ingressHelpPath, ingressDnsHelpPath); this.store.setManagePrometheusPath(managePrometheusPath); this.store.updateStatus(clusterStatus); this.store.updateStatusReason(clusterStatusReason); @@ -98,6 +99,7 @@ export default class Clusters { helpPath: this.state.helpPath, ingressHelpPath: this.state.ingressHelpPath, managePrometheusPath: this.state.managePrometheusPath, + ingressDnsHelpPath: this.state.ingressDnsHelpPath, }, }); }, diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index 50e35bbbba5..c2a35341eb2 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -36,10 +36,6 @@ type: String, required: false, }, - description: { - type: String, - required: true, - }, status: { type: String, required: false, @@ -148,7 +144,7 @@ class="table-section section-wrap" role="gridcell" > - <div v-html="description"></div> + <slot name="description"></slot> </div> <div class="table-section table-button-footer section-align-top" diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 978881a4831..35618398468 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -2,10 +2,16 @@ import _ from 'underscore'; import { s__, sprintf } from '../../locale'; import applicationRow from './application_row.vue'; + import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; + import { + APPLICATION_INSTALLED, + INGRESS, + } from '../constants'; export default { components: { applicationRow, + clipboardButton, }, props: { applications: { @@ -23,6 +29,11 @@ required: false, default: '', }, + ingressDnsHelpPath: { + type: String, + required: false, + default: '', + }, managePrometheusPath: { type: String, required: false, @@ -43,19 +54,16 @@ false, ); }, - helmTillerDescription() { - return _.escape(s__( - `ClusterIntegration|Helm streamlines installing and managing Kubernetes applications. - Tiller runs inside of your Kubernetes Cluster, and manages - releases of your charts.`, - )); + ingressId() { + return INGRESS; + }, + ingressInstalled() { + return this.applications.ingress.status === APPLICATION_INSTALLED; + }, + ingressExternalIp() { + return this.applications.ingress.externalIp; }, ingressDescription() { - const descriptionParagraph = _.escape(s__( - `ClusterIntegration|Ingress gives you a way to route requests to services based on the - request host or path, centralizing a number of services into a single entrypoint.`, - )); - const extraCostParagraph = sprintf( _.escape(s__( `ClusterIntegration|%{boldNotice} This will add some extra resources @@ -84,9 +92,6 @@ return ` <p> - ${descriptionParagraph} - </p> - <p> ${extraCostParagraph} </p> <p class="settings-message append-bottom-0"> @@ -136,33 +141,121 @@ id="helm" :title="applications.helm.title" title-link="https://docs.helm.sh/" - :description="helmTillerDescription" :status="applications.helm.status" :status-reason="applications.helm.statusReason" :request-status="applications.helm.requestStatus" :request-reason="applications.helm.requestReason" - /> + > + <div slot="description"> + {{ s__(`ClusterIntegration|Helm streamlines installing + and managing Kubernetes applications. + Tiller runs inside of your Kubernetes Cluster, + and manages releases of your charts.`) }} + </div> + </application-row> <application-row - id="ingress" + :id="ingressId" :title="applications.ingress.title" title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/" - :description="ingressDescription" :status="applications.ingress.status" :status-reason="applications.ingress.statusReason" :request-status="applications.ingress.requestStatus" :request-reason="applications.ingress.requestReason" - /> + > + <div slot="description"> + <p> + {{ s__(`ClusterIntegration|Ingress gives you a way to route + requests to services based on the request host or path, + centralizing a number of services into a single entrypoint.`) }} + </p> + + <template v-if="ingressInstalled"> + <div class="form-group"> + <label for="ingress-ip-address"> + {{ s__('ClusterIntegration|Ingress IP Address') }} + </label> + <div + v-if="ingressExternalIp" + class="input-group" + > + <input + type="text" + id="ingress-ip-address" + class="form-control js-ip-address" + :value="ingressExternalIp" + readonly + /> + <span class="input-group-btn"> + <clipboard-button + :text="ingressExternalIp" + :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')" + css-class="btn btn-default js-clipboard-btn" + /> + </span> + </div> + <input + v-else + type="text" + class="form-control js-ip-address" + readonly + value="?" + /> + </div> + + <p + v-if="!ingressExternalIp" + class="settings-message js-no-ip-message" + > + {{ s__(`ClusterIntegration|The IP address is in + the process of being assigned. Please check your Kubernetes + cluster or Quotas on GKE if it takes a long time.`) }} + + <a + :href="ingressHelpPath" + target="_blank" + rel="noopener noreferrer" + > + {{ __('More information') }} + </a> + </p> + + <p> + {{ s__(`ClusterIntegration|Point a wildcard DNS to this + generated IP address in order to access + your application after it has been deployed.`) }} + <a + :href="ingressDnsHelpPath" + target="_blank" + rel="noopener noreferrer" + > + {{ __('More information') }} + </a> + </p> + + </template> + <div + v-else + v-html="ingressDescription" + > + </div> + </div> + </application-row> <application-row id="prometheus" :title="applications.prometheus.title" title-link="https://prometheus.io/docs/introduction/overview/" :manage-link="managePrometheusPath" - :description="prometheusDescription" :status="applications.prometheus.status" :status-reason="applications.prometheus.statusReason" :request-status="applications.prometheus.requestStatus" :request-reason="applications.prometheus.requestReason" - /> + > + <div + slot="description" + v-html="prometheusDescription" + > + </div> + </application-row> <!-- NOTE: Don't forget to update `clusters.scss` min-height for this block and uncomment `application_spec` tests diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js index 93223aefff8..b7179f52bb3 100644 --- a/app/assets/javascripts/clusters/constants.js +++ b/app/assets/javascripts/clusters/constants.js @@ -10,3 +10,4 @@ export const APPLICATION_ERROR = 'errored'; export const REQUEST_LOADING = 'request-loading'; export const REQUEST_SUCCESS = 'request-success'; export const REQUEST_FAILURE = 'request-failure'; +export const INGRESS = 'ingress'; diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index 904ee5fd475..348bbec3b25 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -1,4 +1,5 @@ import { s__ } from '../../locale'; +import { INGRESS } from '../constants'; export default class ClusterStore { constructor() { @@ -21,6 +22,7 @@ export default class ClusterStore { statusReason: null, requestStatus: null, requestReason: null, + externalIp: null, }, runner: { title: s__('ClusterIntegration|GitLab Runner'), @@ -40,9 +42,10 @@ export default class ClusterStore { }; } - setHelpPaths(helpPath, ingressHelpPath) { + setHelpPaths(helpPath, ingressHelpPath, ingressDnsHelpPath) { this.state.helpPath = helpPath; this.state.ingressHelpPath = ingressHelpPath; + this.state.ingressDnsHelpPath = ingressDnsHelpPath; } setManagePrometheusPath(managePrometheusPath) { @@ -64,6 +67,7 @@ export default class ClusterStore { updateStateFromServer(serverState = {}) { this.state.status = serverState.status; this.state.statusReason = serverState.status_reason; + serverState.applications.forEach((serverAppEntry) => { const { name: appId, @@ -76,6 +80,10 @@ export default class ClusterStore { status, statusReason, }; + + if (appId === INGRESS) { + this.state.applications.ingress.externalIp = serverAppEntry.external_ip; + } }); } } diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js index a3cc04e35fe..fd42f9c3baa 100644 --- a/app/assets/javascripts/u2f/authenticate.js +++ b/app/assets/javascripts/u2f/authenticate.js @@ -1,7 +1,5 @@ -/* eslint-disable func-names, wrap-iife */ -/* global u2f */ import _ from 'underscore'; -import isU2FSupported from './util'; +import importU2FLibrary from './util'; import U2FError from './error'; // Authenticate U2F (universal 2nd factor) devices for users to authenticate with. @@ -10,6 +8,7 @@ import U2FError from './error'; // State Flow #2: setup -> in_progress -> error -> setup export default class U2FAuthenticate { constructor(container, form, u2fParams, fallbackButton, fallbackUI) { + this.u2fUtils = null; this.container = container; this.renderNotSupported = this.renderNotSupported.bind(this); this.renderAuthenticated = this.renderAuthenticated.bind(this); @@ -50,22 +49,23 @@ export default class U2FAuthenticate { } start() { - if (isU2FSupported()) { - return this.renderInProgress(); - } - return this.renderNotSupported(); + return importU2FLibrary() + .then((utils) => { + this.u2fUtils = utils; + this.renderInProgress(); + }) + .catch(() => this.renderNotSupported()); } authenticate() { - return u2f.sign(this.appId, this.challenge, this.signRequests, (function (_this) { - return function (response) { + return this.u2fUtils.sign(this.appId, this.challenge, this.signRequests, + (response) => { if (response.errorCode) { const error = new U2FError(response.errorCode, 'authenticate'); - return _this.renderError(error); + return this.renderError(error); } - return _this.renderAuthenticated(JSON.stringify(response)); - }; - })(this), 10); + return this.renderAuthenticated(JSON.stringify(response)); + }, 10); } renderTemplate(name, params) { diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js index cc3f02e75f6..869fac658e8 100644 --- a/app/assets/javascripts/u2f/register.js +++ b/app/assets/javascripts/u2f/register.js @@ -1,8 +1,5 @@ -/* eslint-disable func-names, wrap-iife */ -/* global u2f */ - import _ from 'underscore'; -import isU2FSupported from './util'; +import importU2FLibrary from './util'; import U2FError from './error'; // Register U2F (universal 2nd factor) devices for users to authenticate with. @@ -11,6 +8,7 @@ import U2FError from './error'; // State Flow #2: setup -> in_progress -> error -> setup export default class U2FRegister { constructor(container, u2fParams) { + this.u2fUtils = null; this.container = container; this.renderNotSupported = this.renderNotSupported.bind(this); this.renderRegistered = this.renderRegistered.bind(this); @@ -34,22 +32,23 @@ export default class U2FRegister { } start() { - if (isU2FSupported()) { - return this.renderSetup(); - } - return this.renderNotSupported(); + return importU2FLibrary() + .then((utils) => { + this.u2fUtils = utils; + this.renderSetup(); + }) + .catch(() => this.renderNotSupported()); } register() { - return u2f.register(this.appId, this.registerRequests, this.signRequests, (function (_this) { - return function (response) { + return this.u2fUtils.register(this.appId, this.registerRequests, this.signRequests, + (response) => { if (response.errorCode) { const error = new U2FError(response.errorCode, 'register'); - return _this.renderError(error); + return this.renderError(error); } - return _this.renderRegistered(JSON.stringify(response)); - }; - })(this), 10); + return this.renderRegistered(JSON.stringify(response)); + }, 10); } renderTemplate(name, params) { diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js index 9771ff935c2..5778f00332d 100644 --- a/app/assets/javascripts/u2f/util.js +++ b/app/assets/javascripts/u2f/util.js @@ -1,3 +1,41 @@ -export default function isU2FSupported() { - return window.u2f; +function isOpera(userAgent) { + return userAgent.indexOf('Opera') >= 0 || userAgent.indexOf('OPR') >= 0; +} + +function getOperaVersion(userAgent) { + const match = userAgent.match(/OPR[^0-9]*([0-9]+)[^0-9]+/); + return match ? parseInt(match[1], 10) : false; +} + +function isChrome(userAgent) { + return userAgent.indexOf('Chrom') >= 0 && !isOpera(userAgent); +} + +function getChromeVersion(userAgent) { + const match = userAgent.match(/Chrom(?:e|ium)\/([0-9]+)\./); + return match ? parseInt(match[1], 10) : false; +} + +export function canInjectU2fApi(userAgent) { + const isSupportedChrome = isChrome(userAgent) && getChromeVersion(userAgent) >= 41; + const isSupportedOpera = isOpera(userAgent) && getOperaVersion(userAgent) >= 40; + const isMobile = ( + userAgent.indexOf('droid') >= 0 || + userAgent.indexOf('CriOS') >= 0 || + /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) + ); + return (isSupportedChrome || isSupportedOpera) && !isMobile; +} + +export default function importU2FLibrary() { + if (window.u2f) { + return Promise.resolve(window.u2f); + } + + const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : ''; + if (canInjectU2fApi(userAgent) || (gon && gon.test_env)) { + return import(/* webpackMode: "eager" */ 'vendor/u2f').then(() => window.u2f); + } + + return Promise.reject(); } diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index e855ec3c098..3b6c2da1664 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -28,6 +28,11 @@ required: false, default: false, }, + cssClass: { + type: String, + required: false, + default: 'btn btn-default btn-transparent btn-clipboard', + }, }, }; </script> @@ -35,7 +40,7 @@ <template> <button type="button" - class="btn btn-transparent btn-clipboard" + :class="cssClass" :title="title" :data-clipboard-text="text" v-tooltip diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e6a41202f04..7f83bd10e93 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -191,7 +191,7 @@ class ApplicationController < ActionController::Base return unless signed_in? && session[:service_tickets] valid = session[:service_tickets].all? do |provider, ticket| - Gitlab::OAuth::Session.valid?(provider, ticket) + Gitlab::Auth::OAuth::Session.valid?(provider, ticket) end unless valid @@ -215,7 +215,7 @@ class ApplicationController < ActionController::Base if current_user && current_user.requires_ldap_check? return unless current_user.try_obtain_ldap_lease - unless Gitlab::LDAP::Access.allowed?(current_user) + unless Gitlab::Auth::LDAP::Access.allowed?(current_user) sign_out current_user flash[:alert] = "Access denied for your LDAP account." redirect_to new_user_session_path @@ -230,7 +230,7 @@ class ApplicationController < ActionController::Base end def gitlab_ldap_access(&block) - Gitlab::LDAP::Access.open { |access| yield(access) } + Gitlab::Auth::LDAP::Access.open { |access| yield(access) } end # JSON for infinite scroll via Pager object @@ -284,7 +284,7 @@ class ApplicationController < ActionController::Base end def github_import_configured? - Gitlab::OAuth::Provider.enabled?(:github) + Gitlab::Auth::OAuth::Provider.enabled?(:github) end def gitlab_import_enabled? @@ -292,7 +292,7 @@ class ApplicationController < ActionController::Base end def gitlab_import_configured? - Gitlab::OAuth::Provider.enabled?(:gitlab) + Gitlab::Auth::OAuth::Provider.enabled?(:gitlab) end def bitbucket_import_enabled? @@ -300,7 +300,7 @@ class ApplicationController < ActionController::Base end def bitbucket_import_configured? - Gitlab::OAuth::Provider.enabled?(:bitbucket) + Gitlab::Auth::OAuth::Provider.enabled?(:bitbucket) end def google_code_import_enabled? diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 13ea736688d..61d81ad8a71 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -71,7 +71,7 @@ class Import::BitbucketController < Import::BaseController end def provider - Gitlab::OAuth::Provider.config_for('bitbucket') + Gitlab::Auth::OAuth::Provider.config_for('bitbucket') end def options diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 83c9a3f035e..8440945ab43 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -10,8 +10,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end end - if Gitlab::LDAP::Config.enabled? - Gitlab::LDAP::Config.available_servers.each do |server| + if Gitlab::Auth::LDAP::Config.enabled? + Gitlab::Auth::LDAP::Config.available_servers.each do |server| define_method server['provider_name'] do ldap end @@ -31,7 +31,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController # We only find ourselves here # if the authentication to LDAP was successful. def ldap - ldap_user = Gitlab::LDAP::User.new(oauth) + ldap_user = Gitlab::Auth::LDAP::User.new(oauth) ldap_user.save if ldap_user.changed? # will also save new users @user = ldap_user.gl_user @@ -62,13 +62,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_to after_sign_in_path_for(current_user) end else - saml_user = Gitlab::Saml::User.new(oauth) + saml_user = Gitlab::Auth::Saml::User.new(oauth) saml_user.save if saml_user.changed? @user = saml_user.gl_user continue_login_process end - rescue Gitlab::OAuth::SignupDisabledError + rescue Gitlab::Auth::OAuth::User::SignupDisabledError handle_signup_error end @@ -106,20 +106,20 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController log_audit_event(current_user, with: oauth['provider']) redirect_to profile_account_path, notice: 'Authentication method updated' else - oauth_user = Gitlab::OAuth::User.new(oauth) + oauth_user = Gitlab::Auth::OAuth::User.new(oauth) oauth_user.save @user = oauth_user.gl_user continue_login_process end - rescue Gitlab::OAuth::SigninDisabledForProviderError + rescue Gitlab::Auth::OAuth::User::SigninDisabledForProviderError handle_disabled_provider - rescue Gitlab::OAuth::SignupDisabledError + rescue Gitlab::Auth::OAuth::User::SignupDisabledError handle_signup_error end def handle_service_ticket(provider, ticket) - Gitlab::OAuth::Session.create provider, ticket + Gitlab::Auth::OAuth::Session.create provider, ticket session[:service_tickets] ||= {} session[:service_tickets][provider] = ticket end @@ -142,7 +142,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end def handle_signup_error - label = Gitlab::OAuth::Provider.label_for(oauth['provider']) + label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider']) message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed." if Gitlab::CurrentSettings.allow_signup? @@ -171,7 +171,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end def handle_disabled_provider - label = Gitlab::OAuth::Provider.label_for(oauth['provider']) + label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider']) flash[:alert] = "Signing in using #{label} has been disabled" redirect_to new_user_session_path diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 142e8b6e4bc..aeaba3a0acf 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -4,6 +4,7 @@ class Projects::ClustersController < Projects::ApplicationController before_action :authorize_create_cluster!, only: [:new] before_action :authorize_update_cluster!, only: [:update] before_action :authorize_admin_cluster!, only: [:destroy] + before_action :update_applications_status, only: [:status] STATUS_POLLING_INTERVAL = 10_000 @@ -114,4 +115,8 @@ class Projects::ClustersController < Projects::ApplicationController def authorize_admin_cluster! access_denied! unless can?(current_user, :admin_cluster, cluster) end + + def update_applications_status + @cluster.applications.each(&:schedule_status_update) + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index c73306a6b66..f3a4aa849c7 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -16,7 +16,7 @@ class SessionsController < Devise::SessionsController def new set_minimum_password_length - @ldap_servers = Gitlab::LDAP::Config.available_servers + @ldap_servers = Gitlab::Auth::LDAP::Config.available_servers super end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index ab68ecad2ba..4c4d7cca8a5 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -77,7 +77,7 @@ module ApplicationSettingsHelper label_tag(checkbox_name, class: css_class) do check_box_tag(checkbox_name, source, !disabled, - autocomplete: 'off') + Gitlab::OAuth::Provider.label_for(source) + autocomplete: 'off') + Gitlab::Auth::OAuth::Provider.label_for(source) end end end diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index f909f664034..c109954f3a3 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -3,7 +3,7 @@ module AuthHelper FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze def ldap_enabled? - Gitlab::LDAP::Config.enabled? + Gitlab::Auth::LDAP::Config.enabled? end def omniauth_enabled? @@ -15,11 +15,11 @@ module AuthHelper end def auth_providers - Gitlab::OAuth::Provider.providers + Gitlab::Auth::OAuth::Provider.providers end def label_for_provider(name) - Gitlab::OAuth::Provider.label_for(name) + Gitlab::Auth::OAuth::Provider.label_for(name) end def form_based_provider?(name) diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index 5a4fda0724c..e7aa92e6e5c 100644 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -3,7 +3,7 @@ module ProfilesHelper user_synced_attributes_metadata = current_user.user_synced_attributes_metadata if user_synced_attributes_metadata&.synced?(attribute) if user_synced_attributes_metadata.provider - Gitlab::OAuth::Provider.label_for(user_synced_attributes_metadata.provider) + Gitlab::Auth::OAuth::Provider.label_for(user_synced_attributes_metadata.provider) else 'LDAP' end diff --git a/app/helpers/u2f_helper.rb b/app/helpers/u2f_helper.rb deleted file mode 100644 index 81bfe5d4eeb..00000000000 --- a/app/helpers/u2f_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module U2fHelper - def inject_u2f_api? - ((browser.chrome? && browser.version.to_i >= 41) || (browser.opera? && browser.version.to_i >= 40)) && !browser.device.mobile? - end -end diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index aa5cf97756f..9f583342c19 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -5,6 +5,7 @@ module Clusters include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus + include AfterCommitQueue default_value_for :ingress_type, :nginx default_value_for :version, :nginx @@ -13,6 +14,17 @@ module Clusters nginx: 1 } + FETCH_IP_ADDRESS_DELAY = 30.seconds + + state_machine :status do + before_transition any => [:installed] do |application| + application.run_after_commit do + ClusterWaitForIngressIpAddressWorker.perform_in( + FETCH_IP_ADDRESS_DELAY, application.name, application.id) + end + end + end + def chart 'stable/nginx-ingress' end @@ -24,6 +36,13 @@ module Clusters def install_command Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file) end + + def schedule_status_update + return unless installed? + return if external_ip + + ClusterWaitForIngressIpAddressWorker.perform_async(name, id) + end end end end diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb index a98fa85a5ff..623b836c0ed 100644 --- a/app/models/clusters/concerns/application_core.rb +++ b/app/models/clusters/concerns/application_core.rb @@ -23,6 +23,11 @@ module Clusters def name self.class.application_name end + + def schedule_status_update + # Override if you need extra data synchronized + # from K8s after installation + end end end end diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb index d2e626c22e8..b34d1382d43 100644 --- a/app/models/cycle_analytics.rb +++ b/app/models/cycle_analytics.rb @@ -6,6 +6,12 @@ class CycleAnalytics @options = options end + def all_medians_per_stage + STAGES.each_with_object({}) do |stage_name, medians_per_stage| + medians_per_stage[stage_name] = self[stage_name].median + end + end + def summary @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(@project, from: @options[:from], diff --git a/app/models/identity.rb b/app/models/identity.rb index 2b433e9b988..1011b9f1109 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -17,12 +17,12 @@ class Identity < ActiveRecord::Base end def ldap? - Gitlab::OAuth::Provider.ldap_provider?(provider) + Gitlab::Auth::OAuth::Provider.ldap_provider?(provider) end def self.normalize_uid(provider, uid) - if Gitlab::OAuth::Provider.ldap_provider?(provider) - Gitlab::LDAP::Person.normalize_dn(uid) + if Gitlab::Auth::OAuth::Provider.ldap_provider?(provider) + Gitlab::Auth::LDAP::Person.normalize_dn(uid) else uid.to_s end diff --git a/app/models/repository.rb b/app/models/repository.rb index 7888c1019e6..242d9d5f125 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -590,15 +590,7 @@ class Repository def license_key return unless exists? - # The licensee gem creates a Rugged object from the path: - # https://github.com/benbalter/licensee/blob/v8.7.0/lib/licensee/projects/git_project.rb - begin - Licensee.license(path).try(:key) - # Normally we would rescue Rugged::Error, but that is banned by lint-rugged - # and we need to migrate this endpoint to Gitaly: - # https://gitlab.com/gitlab-org/gitaly/issues/1026 - rescue - end + raw_repository.license_short_name end cache_method :license_key diff --git a/app/models/user.rb b/app/models/user.rb index 982080763d2..9547506d33d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -728,7 +728,7 @@ class User < ActiveRecord::Base def ldap_user? if identities.loaded? - identities.find { |identity| Gitlab::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? } + identities.find { |identity| Gitlab::Auth::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? } else identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) end diff --git a/app/models/user_synced_attributes_metadata.rb b/app/models/user_synced_attributes_metadata.rb index 548b99b69d9..688432a9d67 100644 --- a/app/models/user_synced_attributes_metadata.rb +++ b/app/models/user_synced_attributes_metadata.rb @@ -26,6 +26,6 @@ class UserSyncedAttributesMetadata < ActiveRecord::Base private def sync_profile_from_provider? - Gitlab::OAuth::Provider.sync_profile_from_provider?(provider) + Gitlab::Auth::OAuth::Provider.sync_profile_from_provider?(provider) end end diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb index 564612202b5..3e355a13e06 100644 --- a/app/serializers/analytics_stage_entity.rb +++ b/app/serializers/analytics_stage_entity.rb @@ -7,6 +7,7 @@ class AnalyticsStageEntity < Grape::Entity expose :description expose :median, as: :value do |stage| - stage.median && !stage.median.zero? ? distance_of_time_in_words(stage.median) : nil + # median returns a BatchLoader instance which we first have to unwrap by using to_i + !stage.median.to_i.zero? ? distance_of_time_in_words(stage.median) : nil end end diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 3f9a275ad08..b22a0b666ef 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -2,4 +2,5 @@ class ClusterApplicationEntity < Grape::Entity expose :name expose :status_name, as: :status expose :status_reason + expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) } end diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb new file mode 100644 index 00000000000..e572b1e5d99 --- /dev/null +++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb @@ -0,0 +1,36 @@ +module Clusters + module Applications + class CheckIngressIpAddressService < BaseHelmService + include Gitlab::Utils::StrongMemoize + + Error = Class.new(StandardError) + + LEASE_TIMEOUT = 15.seconds.to_i + + def execute + return if app.external_ip + return unless try_obtain_lease + + app.update!(external_ip: ingress_ip) if ingress_ip + end + + private + + def try_obtain_lease + Gitlab::ExclusiveLease + .new("check_ingress_ip_address_service:#{app.id}", timeout: LEASE_TIMEOUT) + .try_obtain + end + + def ingress_ip + service.status.loadBalancer.ingress&.first&.ip + end + + def service + strong_memoize(:ingress_service) do + kubeclient.get_service('ingress-nginx-ingress-controller', Gitlab::Kubernetes::Helm::NAMESPACE) + end + end + end + end +end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 20527d31870..b89b7a9ff85 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -173,7 +173,7 @@ Password authentication enabled for Git over HTTP(S) .help-block When disabled, a Personal Access Token - - if Gitlab::LDAP::Config.enabled? + - if Gitlab::Auth::LDAP::Config.enabled? or LDAP password must be used to authenticate. - if omniauth_enabled? && button_based_providers.any? diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml index 112a201fafa..5381b854f5c 100644 --- a/app/views/admin/identities/_form.html.haml +++ b/app/views/admin/identities/_form.html.haml @@ -4,7 +4,7 @@ .form-group = f.label :provider, class: 'control-label' .col-sm-10 - - values = Gitlab::OAuth::Provider.providers.map { |name| ["#{Gitlab::OAuth::Provider.label_for(name)} (#{name})", name] } + - values = Gitlab::Auth::OAuth::Provider.providers.map { |name| ["#{Gitlab::Auth::OAuth::Provider.label_for(name)} (#{name})", name] } = f.select :provider, values, { allow_blank: false }, class: 'form-control' .form-group = f.label :extern_uid, "Identifier", class: 'control-label' diff --git a/app/views/admin/identities/_identity.html.haml b/app/views/admin/identities/_identity.html.haml index 8c658905bd6..ef5a3f1d969 100644 --- a/app/views/admin/identities/_identity.html.haml +++ b/app/views/admin/identities/_identity.html.haml @@ -1,6 +1,6 @@ %tr %td - #{Gitlab::OAuth::Provider.label_for(identity.provider)} (#{identity.provider}) + #{Gitlab::Auth::OAuth::Provider.label_for(identity.provider)} (#{identity.provider}) %td = identity.extern_uid %td diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 56ec1b3db0d..6e54b9b5645 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -1,7 +1,3 @@ -- if inject_u2f_api? - - content_for :page_specific_javascripts do - = webpack_bundle_tag('u2f') - %div = render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication' .login-box diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 8707af36e2e..329bf16895f 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -4,8 +4,6 @@ - content_for :page_specific_javascripts do - - if inject_u2f_api? - = webpack_bundle_tag('u2f') = webpack_bundle_tag('two_factor_auth') .js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path } diff --git a/app/views/projects/blob/_viewer.html.haml b/app/views/projects/blob/_viewer.html.haml index cc85e5de40f..3124443b4e4 100644 --- a/app/views/projects/blob/_viewer.html.haml +++ b/app/views/projects/blob/_viewer.html.haml @@ -1,9 +1,10 @@ - hidden = local_assigns.fetch(:hidden, false) - render_error = viewer.render_error +- rich_type = viewer.type == :rich ? viewer.partial_name : nil - load_async = local_assigns.fetch(:load_async, viewer.load_async? && render_error.nil?) - viewer_url = local_assigns.fetch(:viewer_url) { url_for(params.merge(viewer: viewer.type, format: :json)) } if load_async -.blob-viewer{ data: { type: viewer.type, url: viewer_url }, class: ('hidden' if hidden) } +.blob-viewer{ data: { type: viewer.type, rich_type: rich_type, url: viewer_url }, class: ('hidden' if hidden) } - if render_error = render 'projects/blob/render_error', viewer: viewer - elsif load_async diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml index 15349387eb2..b20106e8c3a 100644 --- a/app/views/projects/blob/viewers/_balsamiq.html.haml +++ b/app/views/projects/blob/viewers/_balsamiq.html.haml @@ -1,4 +1 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('balsamiq_viewer') - .file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_notebook.html.haml b/app/views/projects/blob/viewers/_notebook.html.haml index d1ffaca35b9..eb4ca1b9816 100644 --- a/app/views/projects/blob/viewers/_notebook.html.haml +++ b/app/views/projects/blob/viewers/_notebook.html.haml @@ -1,5 +1 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('notebook_viewer') - .file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_pdf.html.haml b/app/views/projects/blob/viewers/_pdf.html.haml index fc3f0d922b1..95d837a57dc 100644 --- a/app/views/projects/blob/viewers/_pdf.html.haml +++ b/app/views/projects/blob/viewers/_pdf.html.haml @@ -1,5 +1 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('pdf_viewer') - .file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_sketch.html.haml b/app/views/projects/blob/viewers/_sketch.html.haml index 8fb67c819c1..b4b6492b92f 100644 --- a/app/views/projects/blob/viewers/_sketch.html.haml +++ b/app/views/projects/blob/viewers/_sketch.html.haml @@ -1,7 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('sketch_viewer') - .file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } } .js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' } = icon('spinner spin 2x', 'aria-hidden' => 'true'); diff --git a/app/views/projects/blob/viewers/_stl.html.haml b/app/views/projects/blob/viewers/_stl.html.haml index e58809ec008..55dd8cba7fe 100644 --- a/app/views/projects/blob/viewers/_stl.html.haml +++ b/app/views/projects/blob/viewers/_stl.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('stl_viewer') - .file-content.is-stl-loading .text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } } = icon('spinner spin 2x', class: 'prepend-top-default append-bottom-default', 'aria-hidden' => 'true', 'aria-label' => 'Loading') diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml index 2b1b23ba198..179c45a9867 100644 --- a/app/views/projects/clusters/show.html.haml +++ b/app/views/projects/clusters/show.html.haml @@ -15,6 +15,7 @@ cluster_status_reason: @cluster.status_reason, help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'), ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'), + ingress_dns_help_path: help_page_path('topics/autodevops/quick_start_guide.md', anchor: 'point-dns-at-cluster-ip'), manage_prometheus_path: edit_project_service_path(@cluster.project, 'prometheus') } } .js-cluster-application-notice diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index a9415410f8a..328db19be29 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -24,6 +24,7 @@ - gcp_cluster:cluster_wait_for_app_installation - gcp_cluster:wait_for_cluster_creation - gcp_cluster:check_gcp_project_billing +- gcp_cluster:cluster_wait_for_ingress_ip_address - github_import_advance_stage - github_importer:github_import_import_diff_note diff --git a/app/workers/cluster_wait_for_ingress_ip_address_worker.rb b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb new file mode 100644 index 00000000000..8ba5951750c --- /dev/null +++ b/app/workers/cluster_wait_for_ingress_ip_address_worker.rb @@ -0,0 +1,11 @@ +class ClusterWaitForIngressIpAddressWorker + include ApplicationWorker + include ClusterQueue + include ClusterApplications + + def perform(app_name, app_id) + find_application(app_name, app_id) do |app| + Clusters::Applications::CheckIngressIpAddressService.new(app).execute + end + end +end |