summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Greiling <mike@pixelcog.com>2019-01-11 01:47:30 +0000
committerMike Greiling <mike@pixelcog.com>2019-01-11 01:47:30 +0000
commit89c4d2ae623dbd666e2eaa58812323b81e02b4ea (patch)
tree736843b40db79adbf39553e1e6d521b117f40718
parenta0f77090854b3510543dc42529d7353adab03da0 (diff)
parent1102deb0bd04a1bdf550ed74cf381a6bd400a7e0 (diff)
downloadgitlab-ce-89c4d2ae623dbd666e2eaa58812323b81e02b4ea.tar.gz
Merge branch 'knative-show-page' into 'master'
Initial Serverless Functions detailed view See merge request gitlab-org/gitlab-ce!23863
-rw-r--r--app/assets/javascripts/serverless/components/function_details.vue73
-rw-r--r--app/assets/javascripts/serverless/components/function_row.vue25
-rw-r--r--app/assets/javascripts/serverless/components/functions.vue7
-rw-r--r--app/assets/javascripts/serverless/components/pod_box.vue36
-rw-r--r--app/assets/javascripts/serverless/serverless_bundle.js67
-rw-r--r--app/assets/javascripts/serverless/stores/serverless_details_store.js11
-rw-r--r--app/controllers/projects/serverless/functions_controller.rb31
-rw-r--r--app/finders/projects/serverless/functions_finder.rb31
-rw-r--r--app/models/clusters/applications/knative.rb20
-rw-r--r--app/serializers/projects/serverless/service_entity.rb36
-rw-r--r--app/views/projects/serverless/functions/show.html.haml16
-rw-r--r--changelogs/unreleased/knative-show-page.yml5
-rw-r--r--config/routes/project.rb1
-rw-r--r--doc/user/project/clusters/serverless/img/serverless-details.pngbin0 -> 63347 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/serverless-page.pngbin194708 -> 62369 bytes
-rw-r--r--doc/user/project/clusters/serverless/index.md10
-rw-r--r--locale/gitlab.pot20
-rw-r--r--spec/controllers/projects/serverless/functions_controller_spec.rb38
-rw-r--r--spec/finders/projects/serverless/functions_finder_spec.rb25
-rw-r--r--spec/models/clusters/applications/knative_spec.rb36
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb44
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
new file mode 100644
index 00000000000..61e0735199a
--- /dev/null
+++ b/doc/user/project/clusters/serverless/img/serverless-details.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/serverless-page.png b/doc/user/project/clusters/serverless/img/serverless-page.png
index 960d6e736d6..814b8532205 100644
--- a/doc/user/project/clusters/serverless/img/serverless-page.png
+++ b/doc/user/project/clusters/serverless/img/serverless-page.png
Binary files differ
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