diff options
author | Mike Greiling <mike@pixelcog.com> | 2019-01-11 01:47:30 +0000 |
---|---|---|
committer | Mike Greiling <mike@pixelcog.com> | 2019-01-11 01:47:30 +0000 |
commit | 89c4d2ae623dbd666e2eaa58812323b81e02b4ea (patch) | |
tree | 736843b40db79adbf39553e1e6d521b117f40718 | |
parent | a0f77090854b3510543dc42529d7353adab03da0 (diff) | |
parent | 1102deb0bd04a1bdf550ed74cf381a6bd400a7e0 (diff) | |
download | gitlab-ce-89c4d2ae623dbd666e2eaa58812323b81e02b4ea.tar.gz |
Merge branch 'knative-show-page' into 'master'
Initial Serverless Functions detailed view
See merge request gitlab-org/gitlab-ce!23863
21 files changed, 491 insertions, 41 deletions
diff --git a/app/assets/javascripts/serverless/components/function_details.vue b/app/assets/javascripts/serverless/components/function_details.vue new file mode 100644 index 00000000000..2b1c21f041b --- /dev/null +++ b/app/assets/javascripts/serverless/components/function_details.vue @@ -0,0 +1,73 @@ +<script> +import PodBox from './pod_box.vue'; +import ClipboardButton from '../../vue_shared/components/clipboard_button.vue'; +import Icon from '~/vue_shared/components/icon.vue'; + +export default { + components: { + Icon, + PodBox, + ClipboardButton, + }, + props: { + func: { + type: Object, + required: true, + }, + }, + computed: { + name() { + return this.func.name; + }, + description() { + return this.func.description; + }, + funcUrl() { + return this.func.url; + }, + podCount() { + return this.func.podcount || 0; + }, + }, +}; +</script> + +<template> + <section id="serverless-function-details"> + <h3>{{ name }}</h3> + <div class="append-bottom-default"> + <div v-for="line in description.split('\n')" :key="line">{{ line }}<br /></div> + </div> + <div class="clipboard-group append-bottom-default"> + <div class="label label-monospace">{{ funcUrl }}</div> + <clipboard-button + :text="String(funcUrl)" + :title="s__('ServerlessDetails|Copy URL to clipboard')" + class="input-group-text js-clipboard-btn" + /> + <a + :href="funcUrl" + target="_blank" + rel="noopener noreferrer nofollow" + class="input-group-text btn btn-default" + > + <icon name="external-link" /> + </a> + </div> + + <h4>{{ s__('ServerlessDetails|Kubernetes Pods') }}</h4> + <div v-if="podCount > 0"> + <p> + <b v-if="podCount == 1">{{ podCount }} {{ s__('ServerlessDetails|pod in use') }}</b> + <b v-else>{{ podCount }} {{ s__('ServerlessDetails|pods in use') }}</b> + </p> + <pod-box :count="podCount" /> + <p> + {{ + s__('ServerlessDetails|Number of Kubernetes pods in use over time based on necessity.') + }} + </p> + </div> + <div v-else><p>No pods loaded at this time.</p></div> + </section> +</template> diff --git a/app/assets/javascripts/serverless/components/function_row.vue b/app/assets/javascripts/serverless/components/function_row.vue index 31f5427c771..44bfae388cb 100644 --- a/app/assets/javascripts/serverless/components/function_row.vue +++ b/app/assets/javascripts/serverless/components/function_row.vue @@ -15,8 +15,14 @@ export default { name() { return this.func.name; }, - url() { - return this.func.url; + description() { + return this.func.description; + }, + detailUrl() { + return this.func.detail_url; + }, + environment() { + return this.func.environment_scope; }, image() { return this.func.image; @@ -30,11 +36,20 @@ export default { <template> <div class="gl-responsive-table-row"> - <div class="table-section section-20">{{ name }}</div> - <div class="table-section section-50"> - <a :href="url">{{ url }}</a> + <div class="table-section section-20 section-wrap"> + <a :href="detailUrl">{{ name }}</a> + </div> + <div class="table-section section-10">{{ environment }}</div> + <div class="table-section section-40 section-wrap"> + <span class="line-break">{{ description }}</span> </div> <div class="table-section section-20">{{ image }}</div> <div class="table-section section-10"><timeago :time="timestamp" /></div> </div> </template> + +<style> +.line-break { + white-space: pre; +} +</style> diff --git a/app/assets/javascripts/serverless/components/functions.vue b/app/assets/javascripts/serverless/components/functions.vue index 349e14670b1..9606a78410e 100644 --- a/app/assets/javascripts/serverless/components/functions.vue +++ b/app/assets/javascripts/serverless/components/functions.vue @@ -50,8 +50,11 @@ export default { <div class="table-section section-20" role="rowheader"> {{ s__('Serverless|Function') }} </div> - <div class="table-section section-50" role="rowheader"> - {{ s__('Serverless|Domain') }} + <div class="table-section section-10" role="rowheader"> + {{ s__('Serverless|Cluster Env') }} + </div> + <div class="table-section section-40" role="rowheader"> + {{ s__('Serverless|Description') }} </div> <div class="table-section section-20" role="rowheader"> {{ s__('Serverless|Runtime') }} diff --git a/app/assets/javascripts/serverless/components/pod_box.vue b/app/assets/javascripts/serverless/components/pod_box.vue new file mode 100644 index 00000000000..04d3641bce3 --- /dev/null +++ b/app/assets/javascripts/serverless/components/pod_box.vue @@ -0,0 +1,36 @@ +<script> +export default { + props: { + count: { + type: Number, + required: true, + }, + color: { + type: String, + required: false, + default: 'green', + }, + }, + methods: { + boxOffset(i) { + return 20 * (i - 1); + }, + }, +}; +</script> + +<template> + <svg :width="boxOffset(count + 1)" :height="20"> + <rect + v-for="i in count" + :key="i" + width="15" + height="15" + rx="5" + ry="5" + :fill="color" + :x="boxOffset(i)" + y="0" + /> + </svg> +</template> diff --git a/app/assets/javascripts/serverless/serverless_bundle.js b/app/assets/javascripts/serverless/serverless_bundle.js index 3e3b81ba247..47a510d5fb5 100644 --- a/app/assets/javascripts/serverless/serverless_bundle.js +++ b/app/assets/javascripts/serverless/serverless_bundle.js @@ -4,23 +4,65 @@ import { s__ } from '../locale'; import Flash from '../flash'; import Poll from '../lib/utils/poll'; import ServerlessStore from './stores/serverless_store'; +import ServerlessDetailsStore from './stores/serverless_details_store'; import GetFunctionsService from './services/get_functions_service'; import Functions from './components/functions.vue'; +import FunctionDetails from './components/function_details.vue'; export default class Serverless { constructor() { - const { statusPath, clustersPath, helpPath, installed } = document.querySelector( - '.js-serverless-functions-page', - ).dataset; + if (document.querySelector('.js-serverless-function-details-page') != null) { + const { + serviceName, + serviceDescription, + serviceEnvironment, + serviceUrl, + serviceNamespace, + servicePodcount, + } = document.querySelector('.js-serverless-function-details-page').dataset; + const el = document.querySelector('#js-serverless-function-details'); + this.store = new ServerlessDetailsStore(); + const { store } = this; - this.service = new GetFunctionsService(statusPath); - this.knativeInstalled = installed !== undefined; - this.store = new ServerlessStore(this.knativeInstalled, clustersPath, helpPath); - this.initServerless(); - this.functionLoadCount = 0; + const service = { + name: serviceName, + description: serviceDescription, + environment: serviceEnvironment, + url: serviceUrl, + namespace: serviceNamespace, + podcount: servicePodcount, + }; - if (statusPath && this.knativeInstalled) { - this.initPolling(); + this.store.updateDetailedFunction(service); + this.functionDetails = new Vue({ + el, + data() { + return { + state: store.state, + }; + }, + render(createElement) { + return createElement(FunctionDetails, { + props: { + func: this.state.functionDetail, + }, + }); + }, + }); + } else { + const { statusPath, clustersPath, helpPath, installed } = document.querySelector( + '.js-serverless-functions-page', + ).dataset; + + this.service = new GetFunctionsService(statusPath); + this.knativeInstalled = installed !== undefined; + this.store = new ServerlessStore(this.knativeInstalled, clustersPath, helpPath); + this.initServerless(); + this.functionLoadCount = 0; + + if (statusPath && this.knativeInstalled) { + this.initPolling(); + } } } @@ -55,7 +97,7 @@ export default class Serverless { resource: this.service, method: 'fetchData', successCallback: data => this.handleSuccess(data), - errorCallback: () => this.handleError(), + errorCallback: () => Serverless.handleError(), }); if (!Visibility.hidden()) { @@ -64,7 +106,7 @@ export default class Serverless { this.service .fetchData() .then(data => this.handleSuccess(data)) - .catch(() => this.handleError()); + .catch(() => Serverless.handleError()); } Visibility.change(() => { @@ -102,5 +144,6 @@ export default class Serverless { } this.functions.$destroy(); + this.functionDetails.$destroy(); } } diff --git a/app/assets/javascripts/serverless/stores/serverless_details_store.js b/app/assets/javascripts/serverless/stores/serverless_details_store.js new file mode 100644 index 00000000000..5394d2cded1 --- /dev/null +++ b/app/assets/javascripts/serverless/stores/serverless_details_store.js @@ -0,0 +1,11 @@ +export default class ServerlessDetailsStore { + constructor() { + this.state = { + functionDetail: {}, + }; + } + + updateDetailedFunction(func) { + this.state.functionDetail = func; + } +} diff --git a/app/controllers/projects/serverless/functions_controller.rb b/app/controllers/projects/serverless/functions_controller.rb index 0af2b7ef343..39eca10134f 100644 --- a/app/controllers/projects/serverless/functions_controller.rb +++ b/app/controllers/projects/serverless/functions_controller.rb @@ -7,19 +7,17 @@ module Projects before_action :authorize_read_cluster! - INDEX_PRIMING_INTERVAL = 10_000 - INDEX_POLLING_INTERVAL = 30_000 + INDEX_PRIMING_INTERVAL = 15_000 + INDEX_POLLING_INTERVAL = 60_000 def index - finder = Projects::Serverless::FunctionsFinder.new(project.clusters) - respond_to do |format| format.json do functions = finder.execute if functions.any? Gitlab::PollingInterval.set_header(response, interval: INDEX_POLLING_INTERVAL) - render json: Projects::Serverless::ServiceSerializer.new(current_user: @current_user).represent(functions) + render json: serialize_function(functions) else Gitlab::PollingInterval.set_header(response, interval: INDEX_PRIMING_INTERVAL) head :no_content @@ -32,6 +30,29 @@ module Projects end end end + + def show + @service = serialize_function(finder.service(params[:environment_id], params[:id])) + return not_found if @service.nil? + + respond_to do |format| + format.json do + render json: @service + end + + format.html + end + end + + private + + def finder + Projects::Serverless::FunctionsFinder.new(project.clusters) + end + + def serialize_function(function) + Projects::Serverless::ServiceSerializer.new(current_user: @current_user, project: project).represent(function) + end end end end diff --git a/app/finders/projects/serverless/functions_finder.rb b/app/finders/projects/serverless/functions_finder.rb index 2b5d67e79d7..2f2816a4a08 100644 --- a/app/finders/projects/serverless/functions_finder.rb +++ b/app/finders/projects/serverless/functions_finder.rb @@ -15,11 +15,40 @@ module Projects clusters_with_knative_installed.exists? end + def service(environment_scope, name) + knative_service(environment_scope, name)&.first + end + private + def knative_service(environment_scope, name) + clusters_with_knative_installed.preload_knative.map do |cluster| + next if environment_scope != cluster.environment_scope + + services = cluster.application_knative.services_for(ns: cluster.platform_kubernetes&.actual_namespace) + .select { |svc| svc["metadata"]["name"] == name } + + add_metadata(cluster, services).first unless services.nil? + end + end + def knative_services clusters_with_knative_installed.preload_knative.map do |cluster| - cluster.application_knative.services_for(ns: cluster.platform_kubernetes&.actual_namespace) + services = cluster.application_knative.services_for(ns: cluster.platform_kubernetes&.actual_namespace) + add_metadata(cluster, services) unless services.nil? + end + end + + def add_metadata(cluster, services) + services.each do |s| + s["environment_scope"] = cluster.environment_scope + s["cluster_id"] = cluster.id + + if services.length == 1 + s["podcount"] = cluster.application_knative.service_pod_details( + cluster.platform_kubernetes&.actual_namespace, + s["metadata"]["name"]).length + end end end diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index c572c8bff44..8d79b041b64 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -41,6 +41,8 @@ module Clusters scope :for_cluster, -> (cluster) { where(cluster: cluster) } + after_save :clear_reactive_cache! + def chart 'knative/knative' end @@ -79,7 +81,7 @@ module Clusters end def calculate_reactive_cache - { services: read_services } + { services: read_services, pods: read_pods } end def ingress_service @@ -87,7 +89,7 @@ module Clusters end def services_for(ns: namespace) - return unless services + return [] unless services return [] unless ns services.select do |service| @@ -95,8 +97,22 @@ module Clusters end end + def service_pod_details(ns, service) + with_reactive_cache do |data| + data[:pods].select { |pod| filter_pods(pod, ns, service) } + end + end + private + def read_pods + cluster.kubeclient.core_client.get_pods.as_json + end + + def filter_pods(pod, namespace, service) + pod["metadata"]["namespace"] == namespace && pod["metadata"]["labels"]["serving.knative.dev/service"] == service + end + def read_services client.get_services.as_json rescue Kubeclient::ResourceNotFoundError diff --git a/app/serializers/projects/serverless/service_entity.rb b/app/serializers/projects/serverless/service_entity.rb index 4f1f62d145b..c98dc1a1c4a 100644 --- a/app/serializers/projects/serverless/service_entity.rb +++ b/app/serializers/projects/serverless/service_entity.rb @@ -13,6 +13,25 @@ module Projects service.dig('metadata', 'namespace') end + expose :environment_scope do |service| + service.dig('environment_scope') + end + + expose :cluster_id do |service| + service.dig('cluster_id') + end + + expose :detail_url do |service| + project_serverless_path( + request.project, + service.dig('environment_scope'), + service.dig('metadata', 'name')) + end + + expose :podcount do |service| + service.dig('podcount') + end + expose :created_at do |service| service.dig('metadata', 'creationTimestamp') end @@ -22,11 +41,24 @@ module Projects end expose :description do |service| - service.dig('spec', 'runLatest', 'configuration', 'revisionTemplate', 'metadata', 'annotations', 'Description') + service.dig( + 'spec', + 'runLatest', + 'configuration', + 'revisionTemplate', + 'metadata', + 'annotations', + 'Description') end expose :image do |service| - service.dig('spec', 'runLatest', 'configuration', 'build', 'template', 'name') + service.dig( + 'spec', + 'runLatest', + 'configuration', + 'build', + 'template', + 'name') end end end diff --git a/app/views/projects/serverless/functions/show.html.haml b/app/views/projects/serverless/functions/show.html.haml new file mode 100644 index 00000000000..29737b7014a --- /dev/null +++ b/app/views/projects/serverless/functions/show.html.haml @@ -0,0 +1,16 @@ +- @no_container = true +- @content_class = "limit-container-width" unless fluid_layout + +- add_to_breadcrumbs('Serverless', project_serverless_functions_path(@project)) + +- page_title @service[:name] + +.serverless-function-details-page.js-serverless-function-details-page{ data: { service: @service.as_json } } +%div{ class: [container_class, ('limit-container-width' unless fluid_layout)] } + .top-area.adjust + .serverless-function-details#js-serverless-function-details + + .js-serverless-function-notice + .flash-container + + .function-holder.js-function-holder.input-group diff --git a/changelogs/unreleased/knative-show-page.yml b/changelogs/unreleased/knative-show-page.yml new file mode 100644 index 00000000000..a48b754940f --- /dev/null +++ b/changelogs/unreleased/knative-show-page.yml @@ -0,0 +1,5 @@ +--- +title: Add Knative detailed view +merge_request: 23863 +author: Chris Baumbauer +type: added diff --git a/config/routes/project.rb b/config/routes/project.rb index e6ecb4bc9d8..797bf6de37b 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -247,6 +247,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end namespace :serverless do + get '/functions/:environment_id/:id', to: 'functions#show' resources :functions, only: [:index] end diff --git a/doc/user/project/clusters/serverless/img/serverless-details.png b/doc/user/project/clusters/serverless/img/serverless-details.png Binary files differnew file mode 100644 index 00000000000..61e0735199a --- /dev/null +++ b/doc/user/project/clusters/serverless/img/serverless-details.png diff --git a/doc/user/project/clusters/serverless/img/serverless-page.png b/doc/user/project/clusters/serverless/img/serverless-page.png Binary files differindex 960d6e736d6..814b8532205 100644 --- a/doc/user/project/clusters/serverless/img/serverless-page.png +++ b/doc/user/project/clusters/serverless/img/serverless-page.png diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md index ffce29f8f81..5e8824513b0 100644 --- a/doc/user/project/clusters/serverless/index.md +++ b/doc/user/project/clusters/serverless/index.md @@ -167,8 +167,8 @@ appear under **Operations > Serverless**. ![serverless page](img/serverless-page.png) -This page contains all functions available for the project, the URL for -accessing the function, and if available, the function's runtime information. +This page contains all functions available for the project, the description for +accessing the function, and, if available, the function's runtime information. The details are derived from the Knative installation inside each of the project's Kubernetes cluster. @@ -184,6 +184,12 @@ The sample function can now be triggered from any HTTP client using a simple `PO Currently, the Serverless page presents all functions available in all clusters registered for the project with Knative installed. +Clicking on the function name will provide additional details such as the +function's URL as well as runtime statistics such as the number of active pods +available to service the request based on load. + +![serverless function details](img/serverless-details.png) + ## Deploying Serverless applications > Introduced in GitLab 11.5. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c8d4433de35..d3f751c7796 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6060,13 +6060,31 @@ msgstr "" msgid "Serverless" msgstr "" +msgid "ServerlessDetails|Copy URL to clipboard" +msgstr "" + +msgid "ServerlessDetails|Kubernetes Pods" +msgstr "" + +msgid "ServerlessDetails|Number of Kubernetes pods in use over time based on necessity." +msgstr "" + +msgid "ServerlessDetails|pod in use" +msgstr "" + +msgid "ServerlessDetails|pods in use" +msgstr "" + msgid "Serverless| In order to start using functions as a service, you must first install Knative on your Kubernetes cluster." msgstr "" msgid "Serverless|An error occurred while retrieving serverless components" msgstr "" -msgid "Serverless|Domain" +msgid "Serverless|Cluster Env" +msgstr "" + +msgid "Serverless|Description" msgstr "" msgid "Serverless|Function" diff --git a/spec/controllers/projects/serverless/functions_controller_spec.rb b/spec/controllers/projects/serverless/functions_controller_spec.rb index a9759c4fbd8..87114d44bce 100644 --- a/spec/controllers/projects/serverless/functions_controller_spec.rb +++ b/spec/controllers/projects/serverless/functions_controller_spec.rb @@ -45,9 +45,45 @@ describe Projects::Serverless::FunctionsController do end end + describe 'GET #show' do + context 'invalid data' do + it 'has a bad function name' do + get :show, params: params({ format: :json, environment_id: "*", id: "foo" }) + expect(response).to have_gitlab_http_status(404) + end + end + + context 'valid data', :use_clean_rails_memory_store_caching do + before do + stub_kubeclient_service_pods + stub_reactive_cache(knative, + { + services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }) + end + + it 'has a valid function name' do + get :show, params: params({ format: :json, environment_id: "*", id: cluster.project.name }) + expect(response).to have_gitlab_http_status(200) + + expect(json_response).to include( + "name" => project.name, + "url" => "http://#{project.name}.#{namespace.namespace}.example.com", + "podcount" => 1 + ) + end + end + end + describe 'GET #index with data', :use_clean_rails_memory_store_caching do before do - stub_reactive_cache(knative, services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"]) + stub_kubeclient_service_pods + stub_reactive_cache(knative, + { + services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }) end it 'has data' do diff --git a/spec/finders/projects/serverless/functions_finder_spec.rb b/spec/finders/projects/serverless/functions_finder_spec.rb index 60d02b12054..35279906854 100644 --- a/spec/finders/projects/serverless/functions_finder_spec.rb +++ b/spec/finders/projects/serverless/functions_finder_spec.rb @@ -29,15 +29,34 @@ describe Projects::Serverless::FunctionsFinder do context 'has knative installed' do let!(:knative) { create(:clusters_applications_knative, :installed, cluster: cluster) } + let(:finder) { described_class.new(project.clusters) } it 'there are no functions' do - expect(described_class.new(project.clusters).execute).to be_empty + expect(finder.execute).to be_empty end it 'there are functions', :use_clean_rails_memory_store_caching do - stub_reactive_cache(knative, services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"]) + stub_kubeclient_service_pods + stub_reactive_cache(knative, + { + services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }) - expect(described_class.new(project.clusters).execute).not_to be_empty + expect(finder.execute).not_to be_empty + end + + it 'has a function', :use_clean_rails_memory_store_caching do + stub_kubeclient_service_pods + stub_reactive_cache(knative, + { + services: kube_knative_services_body(namespace: namespace.namespace, name: cluster.project.name)["items"], + pods: kube_knative_pods_body(cluster.project.name, namespace.namespace)["items"] + }) + + result = finder.service(cluster.environment_scope, cluster.project.name) + expect(result).not_to be_empty + expect(result["metadata"]["name"]).to be_eql(cluster.project.name) end end end diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index 0cf9e10ce04..35818be8deb 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -149,6 +149,35 @@ describe Clusters::Applications::Knative do it { is_expected.to validate_presence_of(:hostname) } end + describe '#service_pod_details' do + let(:cluster) { create(:cluster, :project, :provided_by_gcp) } + let(:service) { cluster.platform_kubernetes } + let(:knative) { create(:clusters_applications_knative, cluster: cluster) } + + let(:namespace) do + create(:cluster_kubernetes_namespace, + cluster: cluster, + cluster_project: cluster.cluster_project, + project: cluster.cluster_project.project) + end + + before do + stub_kubeclient_discover(service.api_url) + stub_kubeclient_knative_services + stub_kubeclient_service_pods + stub_reactive_cache(knative, + { + services: kube_response(kube_knative_services_body), + pods: kube_response(kube_knative_pods_body(cluster.cluster_project.project.name, namespace.namespace)) + }) + synchronous_reactive_cache(knative) + end + + it 'should be able k8s core for pod details' do + expect(knative.service_pod_details(namespace.namespace, cluster.cluster_project.project.name)).not_to be_nil + end + end + describe '#services' do let(:cluster) { create(:cluster, :project, :provided_by_gcp) } let(:service) { cluster.platform_kubernetes } @@ -166,6 +195,7 @@ describe Clusters::Applications::Knative do before do stub_kubeclient_discover(service.api_url) stub_kubeclient_knative_services + stub_kubeclient_service_pods end it 'should have an unintialized cache' do @@ -174,7 +204,11 @@ describe Clusters::Applications::Knative do context 'when using synchronous reactive cache' do before do - stub_reactive_cache(knative, services: kube_response(kube_knative_services_body)) + stub_reactive_cache(knative, + { + services: kube_response(kube_knative_services_body), + pods: kube_response(kube_knative_pods_body(cluster.cluster_project.project.name, namespace.namespace)) + }) synchronous_reactive_cache(knative) end diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index e7d97561bfc..6930b809048 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -20,6 +20,13 @@ module KubernetesHelpers WebMock.stub_request(:get, api_url + '/apis/serving.knative.dev/v1alpha1').to_return(kube_response(kube_v1alpha1_serving_knative_discovery_body)) end + def stub_kubeclient_service_pods(response = nil) + stub_kubeclient_discover(service.api_url) + pods_url = service.api_url + "/api/v1/pods" + + WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response) + end + def stub_kubeclient_pods(response = nil) stub_kubeclient_discover(service.api_url) pods_url = service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods" @@ -212,6 +219,13 @@ module KubernetesHelpers } end + def kube_knative_pods_body(name, namespace) + { + "kind" => "PodList", + "items" => [kube_knative_pod(name: name, namespace: namespace)] + } + end + def kube_knative_services_body(**options) { "kind" => "List", @@ -242,6 +256,28 @@ module KubernetesHelpers } end + # Similar to a kube_pod, but should contain a running service + def kube_knative_pod(name: "kube-pod", namespace: "default", status: "Running") + { + "metadata" => { + "name" => name, + "namespace" => namespace, + "generate_name" => "generated-name-with-suffix", + "creationTimestamp" => "2016-11-25T19:55:19Z", + "labels" => { + "serving.knative.dev/service" => name + } + }, + "spec" => { + "containers" => [ + { "name" => "container-0" }, + { "name" => "container-1" } + ] + }, + "status" => { "phase" => status } + } + end + def kube_deployment(name: "kube-deployment", app: "valid-deployment-label", track: nil) { "metadata" => { @@ -265,10 +301,10 @@ module KubernetesHelpers def kube_service(name: "kubetest", namespace: "default", domain: "example.com") { "metadata" => { - "creationTimestamp" => "2018-11-21T06:16:33Z", - "name" => name, - "namespace" => namespace, - "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}" + "creationTimestamp" => "2018-11-21T06:16:33Z", + "name" => name, + "namespace" => namespace, + "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}" }, "spec" => { "generation" => 2 |