From 20f78421c82d626a3b43924146b7878fe4777ae8 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Thu, 30 Nov 2017 14:26:08 +0100 Subject: Cache the forks in a namespace in the RequestStore On the `show` of a project that is part of a fork network. We check if the user already created a fork of this project in their personal namespace. We do this in several places, so caching the result of this query in the request store prevents us from repeating it. --- spec/controllers/projects_controller_spec.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'spec/controllers') diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index e7ab714c550..39ee5e8691b 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -261,6 +261,29 @@ describe ProjectsController do expect(response).to redirect_to(namespace_project_path) end end + + context 'when the project is forked and has a repository', :request_store do + let(:public_project) { create(:project, :public, :repository) } + + render_views + + before do + # View the project as a user that does not have any rights + sign_in(create(:user)) + + fork_project(public_project) + end + + it 'does not increase the number of queries when the project is forked' do + # When a project is part of a fork network, we check if the `current_user` + # has a fork in their own namespace. We query this several times. Caching + # the result in the RequestStore brings the number of queries for this + # request down from 64 to 59. + + expect { get(:show, namespace_id: public_project.namespace, id: public_project) } + .not_to exceed_query_limit(59) + end + end end describe "#update" do -- cgit v1.2.1 From 3d4ba90c5007cca3e8619afe8f40675962a9bc73 Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Mon, 4 Dec 2017 16:58:44 +0100 Subject: Count occurrences of a specific query in the query recorder. --- spec/controllers/projects_controller_spec.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'spec/controllers') diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 39ee5e8691b..e4b2bbb7c51 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -264,24 +264,22 @@ describe ProjectsController do context 'when the project is forked and has a repository', :request_store do let(:public_project) { create(:project, :public, :repository) } + let(:other_user) { create(:user) } render_views before do # View the project as a user that does not have any rights - sign_in(create(:user)) + sign_in(other_user) fork_project(public_project) end it 'does not increase the number of queries when the project is forked' do - # When a project is part of a fork network, we check if the `current_user` - # has a fork in their own namespace. We query this several times. Caching - # the result in the RequestStore brings the number of queries for this - # request down from 64 to 59. + expected_query = /#{public_project.fork_network.find_forks_in(other_user.namespace).to_sql}/ expect { get(:show, namespace_id: public_project.namespace, id: public_project) } - .not_to exceed_query_limit(59) + .not_to exceed_query_limit(1).for_query(expected_query) end end end -- cgit v1.2.1 From 4b66bdfa1af8fbef5d2af94a62e2522806cc7250 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 5 Dec 2017 12:00:02 +0000 Subject: Second iteration of Move Kubernetes from service to Cluster page --- .../projects/clusters/gcp_controller_spec.rb | 185 ++++++++++++ .../projects/clusters/user_controller_spec.rb | 87 ++++++ .../projects/clusters_controller_spec.rb | 319 ++++++--------------- 3 files changed, 360 insertions(+), 231 deletions(-) create mode 100644 spec/controllers/projects/clusters/gcp_controller_spec.rb create mode 100644 spec/controllers/projects/clusters/user_controller_spec.rb (limited to 'spec/controllers') diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb new file mode 100644 index 00000000000..bb5ef7bb365 --- /dev/null +++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb @@ -0,0 +1,185 @@ +require 'spec_helper' + +describe Projects::Clusters::GcpController do + include AccessMatchersForController + include GoogleApi::CloudPlatformHelpers + + set(:project) { create(:project) } + + describe 'GET login' do + describe 'functionality' do + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + context 'when omniauth has been configured' do + let(:key) { 'secret-key' } + + let(:session_key_for_redirect_uri) do + GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key) + end + + before do + allow(SecureRandom).to receive(:hex).and_return(key) + end + + it 'has authorize_url' do + go + + expect(assigns(:authorize_url)).to include(key) + expect(session[session_key_for_redirect_uri]).to eq(gcp_new_project_clusters_path(project)) + end + end + + context 'when omniauth has not configured' do + before do + stub_omniauth_setting(providers: []) + end + + it 'does not have authorize_url' do + go + + expect(assigns(:authorize_url)).to be_nil + end + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(project) } + it { expect { go }.to be_allowed_for(:master).of(project) } + it { expect { go }.to be_denied_for(:developer).of(project) } + it { expect { go }.to be_denied_for(:reporter).of(project) } + it { expect { go }.to be_denied_for(:guest).of(project) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + + def go + get :login, namespace_id: project.namespace, project_id: project + end + end + + describe 'GET new' do + describe 'functionality' do + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + context 'when access token is valid' do + before do + stub_google_api_validate_token + end + + it 'has new object' do + go + + expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster) + end + end + + context 'when access token is expired' do + before do + stub_google_api_expired_token + end + + it { expect(go).to redirect_to(gcp_login_project_clusters_path(project)) } + end + + context 'when access token is not stored in session' do + it { expect(go).to redirect_to(gcp_login_project_clusters_path(project)) } + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(project) } + it { expect { go }.to be_allowed_for(:master).of(project) } + it { expect { go }.to be_denied_for(:developer).of(project) } + it { expect { go }.to be_denied_for(:reporter).of(project) } + it { expect { go }.to be_denied_for(:guest).of(project) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + + def go + get :new, namespace_id: project.namespace, project_id: project + end + end + + describe 'POST create' do + let(:params) do + { + cluster: { + name: 'new-cluster', + provider_gcp_attributes: { + gcp_project_id: '111' + } + } + } + end + + describe 'functionality' do + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + context 'when access token is valid' do + before do + stub_google_api_validate_token + end + + context 'when creates a cluster on gke' do + it 'creates a new cluster' do + expect(ClusterProvisionWorker).to receive(:perform_async) + expect { go }.to change { Clusters::Cluster.count } + .and change { Clusters::Providers::Gcp.count } + expect(response).to redirect_to(project_cluster_path(project, project.cluster)) + expect(project.cluster).to be_gcp + expect(project.cluster).to be_kubernetes + end + end + end + + context 'when access token is expired' do + before do + stub_google_api_expired_token + end + + it 'redirects to login page' do + expect(go).to redirect_to(gcp_login_project_clusters_path(project)) + end + end + + context 'when access token is not stored in session' do + it 'redirects to login page' do + expect(go).to redirect_to(gcp_login_project_clusters_path(project)) + end + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(project) } + it { expect { go }.to be_allowed_for(:master).of(project) } + it { expect { go }.to be_denied_for(:developer).of(project) } + it { expect { go }.to be_denied_for(:reporter).of(project) } + it { expect { go }.to be_denied_for(:guest).of(project) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + + def go + post :create, params.merge(namespace_id: project.namespace, project_id: project) + end + end +end diff --git a/spec/controllers/projects/clusters/user_controller_spec.rb b/spec/controllers/projects/clusters/user_controller_spec.rb new file mode 100644 index 00000000000..22005e0dc66 --- /dev/null +++ b/spec/controllers/projects/clusters/user_controller_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe Projects::Clusters::UserController do + include AccessMatchersForController + + set(:project) { create(:project) } + + describe 'GET new' do + describe 'functionality' do + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + it 'has new object' do + go + + expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster) + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(project) } + it { expect { go }.to be_allowed_for(:master).of(project) } + it { expect { go }.to be_denied_for(:developer).of(project) } + it { expect { go }.to be_denied_for(:reporter).of(project) } + it { expect { go }.to be_denied_for(:guest).of(project) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + + def go + get :new, namespace_id: project.namespace, project_id: project + end + end + + describe 'POST create' do + let(:params) do + { + cluster: { + name: 'new-cluster', + platform_kubernetes_attributes: { + api_url: 'http://my-url', + token: 'test', + namespace: 'aaa' + } + } + } + end + + describe 'functionality' do + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + context 'when creates a cluster' do + it 'creates a new cluster' do + expect(ClusterProvisionWorker).to receive(:perform_async) + expect { go }.to change { Clusters::Cluster.count } + .and change { Clusters::Platforms::Kubernetes.count } + expect(response).to redirect_to(project_cluster_path(project, project.cluster)) + end + end + end + + describe 'security' do + it { expect { go }.to be_allowed_for(:admin) } + it { expect { go }.to be_allowed_for(:owner).of(project) } + it { expect { go }.to be_allowed_for(:master).of(project) } + it { expect { go }.to be_denied_for(:developer).of(project) } + it { expect { go }.to be_denied_for(:reporter).of(project) } + it { expect { go }.to be_denied_for(:guest).of(project) } + it { expect { go }.to be_denied_for(:user) } + it { expect { go }.to be_denied_for(:external) } + end + + def go + post :create, params.merge(namespace_id: project.namespace, project_id: project) + end + end +end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index ca2bcb2b5ae..66e67652dad 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -4,6 +4,8 @@ describe Projects::ClustersController do include AccessMatchersForController include GoogleApi::CloudPlatformHelpers + set(:project) { create(:project) } + describe 'GET index' do describe 'functionality' do let(:user) { create(:user) } @@ -14,22 +16,18 @@ describe Projects::ClustersController do end context 'when project has a cluster' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } + let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } it { expect(go).to redirect_to(project_cluster_path(project, project.cluster)) } end context 'when project does not have a cluster' do - let(:project) { create(:project) } - it { expect(go).to redirect_to(new_project_cluster_path(project)) } end end describe 'security' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } + let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } it { expect { go }.to be_allowed_for(:admin) } it { expect { go }.to be_allowed_for(:owner).of(project) } @@ -46,198 +44,8 @@ describe Projects::ClustersController do end end - describe 'GET login' do - let(:project) { create(:project) } - - describe 'functionality' do - let(:user) { create(:user) } - - before do - project.add_master(user) - sign_in(user) - end - - context 'when omniauth has been configured' do - let(:key) { 'secere-key' } - - let(:session_key_for_redirect_uri) do - GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(key) - end - - before do - allow(SecureRandom).to receive(:hex).and_return(key) - end - - it 'has authorize_url' do - go - - expect(assigns(:authorize_url)).to include(key) - expect(session[session_key_for_redirect_uri]).to eq(providers_gcp_new_project_clusters_url(project)) - end - end - - context 'when omniauth has not configured' do - before do - stub_omniauth_setting(providers: []) - end - - it 'does not have authorize_url' do - go - - expect(assigns(:authorize_url)).to be_nil - end - end - end - - describe 'security' do - it { expect { go }.to be_allowed_for(:admin) } - it { expect { go }.to be_allowed_for(:owner).of(project) } - it { expect { go }.to be_allowed_for(:master).of(project) } - it { expect { go }.to be_denied_for(:developer).of(project) } - it { expect { go }.to be_denied_for(:reporter).of(project) } - it { expect { go }.to be_denied_for(:guest).of(project) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - - def go - get :login, namespace_id: project.namespace, project_id: project - end - end - - shared_examples 'requires to login' do - it 'redirects to create a cluster' do - subject - - expect(response).to redirect_to(login_project_clusters_path(project)) - end - end - - describe 'GET new_gcp' do - let(:project) { create(:project) } - - describe 'functionality' do - let(:user) { create(:user) } - - before do - project.add_master(user) - sign_in(user) - end - - context 'when access token is valid' do - before do - stub_google_api_validate_token - end - - it 'has new object' do - go - - expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster) - end - end - - context 'when access token is expired' do - before do - stub_google_api_expired_token - end - - it { expect(go).to redirect_to(login_project_clusters_path(project)) } - end - - context 'when access token is not stored in session' do - it { expect(go).to redirect_to(login_project_clusters_path(project)) } - end - end - - describe 'security' do - it { expect { go }.to be_allowed_for(:admin) } - it { expect { go }.to be_allowed_for(:owner).of(project) } - it { expect { go }.to be_allowed_for(:master).of(project) } - it { expect { go }.to be_denied_for(:developer).of(project) } - it { expect { go }.to be_denied_for(:reporter).of(project) } - it { expect { go }.to be_denied_for(:guest).of(project) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - - def go - get :new_gcp, namespace_id: project.namespace, project_id: project - end - end - - describe 'POST create' do - let(:project) { create(:project) } - - let(:params) do - { - cluster: { - name: 'new-cluster', - provider_type: :gcp, - provider_gcp_attributes: { - gcp_project_id: '111' - } - } - } - end - - describe 'functionality' do - let(:user) { create(:user) } - - before do - project.add_master(user) - sign_in(user) - end - - context 'when access token is valid' do - before do - stub_google_api_validate_token - end - - context 'when creates a cluster on gke' do - it 'creates a new cluster' do - expect(ClusterProvisionWorker).to receive(:perform_async) - expect { go }.to change { Clusters::Cluster.count } - expect(response).to redirect_to(project_cluster_path(project, project.cluster)) - end - end - end - - context 'when access token is expired' do - before do - stub_google_api_expired_token - end - - it 'redirects to login page' do - expect(go).to redirect_to(login_project_clusters_path(project)) - end - end - - context 'when access token is not stored in session' do - it 'redirects to login page' do - expect(go).to redirect_to(login_project_clusters_path(project)) - end - end - end - - describe 'security' do - it { expect { go }.to be_allowed_for(:admin) } - it { expect { go }.to be_allowed_for(:owner).of(project) } - it { expect { go }.to be_allowed_for(:master).of(project) } - it { expect { go }.to be_denied_for(:developer).of(project) } - it { expect { go }.to be_denied_for(:reporter).of(project) } - it { expect { go }.to be_denied_for(:guest).of(project) } - it { expect { go }.to be_denied_for(:user) } - it { expect { go }.to be_denied_for(:external) } - end - - def go - post :create, params.merge(namespace_id: project.namespace, project_id: project) - end - end - describe 'GET status' do - let(:cluster) { create(:cluster, :project, :providing_by_gcp) } - let(:project) { cluster.project } + let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } describe 'functionality' do let(:user) { create(:user) } @@ -275,8 +83,7 @@ describe Projects::ClustersController do end describe 'GET show' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } + let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } describe 'functionality' do let(:user) { create(:user) } @@ -313,10 +120,8 @@ describe Projects::ClustersController do end describe 'PUT update' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - - describe 'functionality' do + context 'when cluster is provided by GCP' do + let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let(:user) { create(:user) } before do @@ -324,10 +129,16 @@ describe Projects::ClustersController do sign_in(user) end - context 'when update enabled' do + context 'when changing parameters' do let(:params) do { - cluster: { enabled: false } + cluster: { + enabled: false, + name: 'my-new-cluster-name', + platform_kubernetes_attributes: { + namespace: 'my-namespace' + } + } } end @@ -340,8 +151,14 @@ describe Projects::ClustersController do expect(cluster.enabled).to be_falsey end + it "does not change cluster name" do + go + + expect(cluster.name).to eq('test-cluster') + end + context 'when cluster is being created' do - let(:cluster) { create(:cluster, :project, :providing_by_gcp) } + let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } it "rejects changes" do go @@ -354,11 +171,46 @@ describe Projects::ClustersController do end end + context 'when cluster is provided by user' do + let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) } + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + context 'when changing parameters' do + let(:params) do + { + cluster: { + enabled: false, + name: 'my-new-cluster-name', + platform_kubernetes_attributes: { + namespace: 'my-namespace' + } + } + } + end + + it "updates and redirects back to show page" do + go + + cluster.reload + expect(response).to redirect_to(project_cluster_path(project, project.cluster)) + expect(flash[:notice]).to eq('Cluster was successfully updated.') + expect(cluster.enabled).to be_falsey + expect(cluster.name).to eq('my-new-cluster-name') + expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') + end + end + end + describe 'security' do + set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } + let(:params) do - { - cluster: { enabled: false } - } + { cluster: { enabled: false } } end it { expect { go }.to be_allowed_for(:admin) } @@ -378,10 +230,7 @@ describe Projects::ClustersController do end end - describe 'delete update' do - let(:cluster) { create(:cluster, :project, :provided_by_gcp) } - let(:project) { cluster.project } - + describe 'DELETE destroy' do describe 'functionality' do let(:user) { create(:user) } @@ -390,31 +239,37 @@ describe Projects::ClustersController do sign_in(user) end - it "destroys and redirects back to clusters list" do - expect { go } - .to change { Clusters::Cluster.count }.by(-1) - .and change { Clusters::Platforms::Kubernetes.count }.by(-1) - .and change { Clusters::Providers::Gcp.count }.by(-1) + context 'when cluster is provided by GCP' do + context 'when cluster is created' do + let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } - expect(response).to redirect_to(project_clusters_path(project)) - expect(flash[:notice]).to eq('Cluster integration was successfully removed.') - end + it "destroys and redirects back to clusters list" do + expect { go } + .to change { Clusters::Cluster.count }.by(-1) + .and change { Clusters::Platforms::Kubernetes.count }.by(-1) + .and change { Clusters::Providers::Gcp.count }.by(-1) - context 'when cluster is being created' do - let(:cluster) { create(:cluster, :project, :providing_by_gcp) } + expect(response).to redirect_to(project_clusters_path(project)) + expect(flash[:notice]).to eq('Cluster integration was successfully removed.') + end + end - it "destroys and redirects back to clusters list" do - expect { go } - .to change { Clusters::Cluster.count }.by(-1) - .and change { Clusters::Providers::Gcp.count }.by(-1) + context 'when cluster is being created' do + let!(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } - expect(response).to redirect_to(project_clusters_path(project)) - expect(flash[:notice]).to eq('Cluster integration was successfully removed.') + it "destroys and redirects back to clusters list" do + expect { go } + .to change { Clusters::Cluster.count }.by(-1) + .and change { Clusters::Providers::Gcp.count }.by(-1) + + expect(response).to redirect_to(project_clusters_path(project)) + expect(flash[:notice]).to eq('Cluster integration was successfully removed.') + end end end - context 'when provider is user' do - let(:cluster) { create(:cluster, :project, :provided_by_user) } + context 'when cluster is provided by user' do + let!(:cluster) { create(:cluster, :provided_by_user, projects: [project]) } it "destroys and redirects back to clusters list" do expect { go } @@ -429,6 +284,8 @@ describe Projects::ClustersController do end describe 'security' do + set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } + it { expect { go }.to be_allowed_for(:admin) } it { expect { go }.to be_allowed_for(:owner).of(project) } it { expect { go }.to be_allowed_for(:master).of(project) } -- cgit v1.2.1