Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
1102deb0
Commit
1102deb0
authored
Dec 16, 2018
by
Chris Baumbauer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial Serverless Functions detailed view
parent
71026ffd
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
491 additions
and
41 deletions
+491
-41
app/assets/javascripts/serverless/components/function_details.vue
...ts/javascripts/serverless/components/function_details.vue
+73
-0
app/assets/javascripts/serverless/components/function_row.vue
...assets/javascripts/serverless/components/function_row.vue
+20
-5
app/assets/javascripts/serverless/components/functions.vue
app/assets/javascripts/serverless/components/functions.vue
+5
-2
app/assets/javascripts/serverless/components/pod_box.vue
app/assets/javascripts/serverless/components/pod_box.vue
+36
-0
app/assets/javascripts/serverless/serverless_bundle.js
app/assets/javascripts/serverless/serverless_bundle.js
+55
-12
app/assets/javascripts/serverless/stores/serverless_details_store.js
...javascripts/serverless/stores/serverless_details_store.js
+11
-0
app/controllers/projects/serverless/functions_controller.rb
app/controllers/projects/serverless/functions_controller.rb
+26
-5
app/finders/projects/serverless/functions_finder.rb
app/finders/projects/serverless/functions_finder.rb
+30
-1
app/models/clusters/applications/knative.rb
app/models/clusters/applications/knative.rb
+18
-2
app/serializers/projects/serverless/service_entity.rb
app/serializers/projects/serverless/service_entity.rb
+34
-2
app/views/projects/serverless/functions/show.html.haml
app/views/projects/serverless/functions/show.html.haml
+16
-0
changelogs/unreleased/knative-show-page.yml
changelogs/unreleased/knative-show-page.yml
+5
-0
config/routes/project.rb
config/routes/project.rb
+1
-0
doc/user/project/clusters/serverless/img/serverless-details.png
...er/project/clusters/serverless/img/serverless-details.png
+0
-0
doc/user/project/clusters/serverless/img/serverless-page.png
doc/user/project/clusters/serverless/img/serverless-page.png
+0
-0
doc/user/project/clusters/serverless/index.md
doc/user/project/clusters/serverless/index.md
+8
-2
locale/gitlab.pot
locale/gitlab.pot
+19
-1
spec/controllers/projects/serverless/functions_controller_spec.rb
...trollers/projects/serverless/functions_controller_spec.rb
+37
-1
spec/finders/projects/serverless/functions_finder_spec.rb
spec/finders/projects/serverless/functions_finder_spec.rb
+22
-3
spec/models/clusters/applications/knative_spec.rb
spec/models/clusters/applications/knative_spec.rb
+35
-1
spec/support/helpers/kubernetes_helpers.rb
spec/support/helpers/kubernetes_helpers.rb
+40
-4
No files found.
app/assets/javascripts/serverless/components/function_details.vue
0 → 100644
View file @
1102deb0
<
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
>
app/assets/javascripts/serverless/components/function_row.vue
View file @
1102deb0
...
@@ -15,8 +15,14 @@ export default {
...
@@ -15,8 +15,14 @@ export default {
name
()
{
name
()
{
return
this
.
func
.
name
;
return
this
.
func
.
name
;
},
},
url
()
{
description
()
{
return
this
.
func
.
url
;
return
this
.
func
.
description
;
},
detailUrl
()
{
return
this
.
func
.
detail_url
;
},
environment
()
{
return
this
.
func
.
environment_scope
;
},
},
image
()
{
image
()
{
return
this
.
func
.
image
;
return
this
.
func
.
image
;
...
@@ -30,11 +36,20 @@ export default {
...
@@ -30,11 +36,20 @@ export default {
<
template
>
<
template
>
<div
class=
"gl-responsive-table-row"
>
<div
class=
"gl-responsive-table-row"
>
<div
class=
"table-section section-20"
>
{{
name
}}
</div>
<div
class=
"table-section section-20 section-wrap"
>
<div
class=
"table-section section-50"
>
<a
:href=
"detailUrl"
>
{{
name
}}
</a>
<a
:href=
"url"
>
{{
url
}}
</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>
<div
class=
"table-section section-20"
>
{{
image
}}
</div>
<div
class=
"table-section section-20"
>
{{
image
}}
</div>
<div
class=
"table-section section-10"
><timeago
:time=
"timestamp"
/></div>
<div
class=
"table-section section-10"
><timeago
:time=
"timestamp"
/></div>
</div>
</div>
</
template
>
</
template
>
<
style
>
.line-break
{
white-space
:
pre
;
}
</
style
>
app/assets/javascripts/serverless/components/functions.vue
View file @
1102deb0
...
@@ -50,8 +50,11 @@ export default {
...
@@ -50,8 +50,11 @@ export default {
<div
class=
"table-section section-20"
role=
"rowheader"
>
<div
class=
"table-section section-20"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Function
'
)
}}
{{
s__
(
'
Serverless|Function
'
)
}}
</div>
</div>
<div
class=
"table-section section-50"
role=
"rowheader"
>
<div
class=
"table-section section-10"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Domain
'
)
}}
{{
s__
(
'
Serverless|Cluster Env
'
)
}}
</div>
<div
class=
"table-section section-40"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Description
'
)
}}
</div>
</div>
<div
class=
"table-section section-20"
role=
"rowheader"
>
<div
class=
"table-section section-20"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Runtime
'
)
}}
{{
s__
(
'
Serverless|Runtime
'
)
}}
...
...
app/assets/javascripts/serverless/components/pod_box.vue
0 → 100644
View file @
1102deb0
<
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
>
app/assets/javascripts/serverless/serverless_bundle.js
View file @
1102deb0
...
@@ -4,11 +4,52 @@ import { s__ } from '../locale';
...
@@ -4,11 +4,52 @@ import { s__ } from '../locale';
import
Flash
from
'
../flash
'
;
import
Flash
from
'
../flash
'
;
import
Poll
from
'
../lib/utils/poll
'
;
import
Poll
from
'
../lib/utils/poll
'
;
import
ServerlessStore
from
'
./stores/serverless_store
'
;
import
ServerlessStore
from
'
./stores/serverless_store
'
;
import
ServerlessDetailsStore
from
'
./stores/serverless_details_store
'
;
import
GetFunctionsService
from
'
./services/get_functions_service
'
;
import
GetFunctionsService
from
'
./services/get_functions_service
'
;
import
Functions
from
'
./components/functions.vue
'
;
import
Functions
from
'
./components/functions.vue
'
;
import
FunctionDetails
from
'
./components/function_details.vue
'
;
export
default
class
Serverless
{
export
default
class
Serverless
{
constructor
()
{
constructor
()
{
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
;
const
service
=
{
name
:
serviceName
,
description
:
serviceDescription
,
environment
:
serviceEnvironment
,
url
:
serviceUrl
,
namespace
:
serviceNamespace
,
podcount
:
servicePodcount
,
};
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
(
const
{
statusPath
,
clustersPath
,
helpPath
,
installed
}
=
document
.
querySelector
(
'
.js-serverless-functions-page
'
,
'
.js-serverless-functions-page
'
,
).
dataset
;
).
dataset
;
...
@@ -23,6 +64,7 @@ export default class Serverless {
...
@@ -23,6 +64,7 @@ export default class Serverless {
this
.
initPolling
();
this
.
initPolling
();
}
}
}
}
}
initServerless
()
{
initServerless
()
{
const
{
store
}
=
this
;
const
{
store
}
=
this
;
...
@@ -55,7 +97,7 @@ export default class Serverless {
...
@@ -55,7 +97,7 @@ export default class Serverless {
resource
:
this
.
service
,
resource
:
this
.
service
,
method
:
'
fetchData
'
,
method
:
'
fetchData
'
,
successCallback
:
data
=>
this
.
handleSuccess
(
data
),
successCallback
:
data
=>
this
.
handleSuccess
(
data
),
errorCallback
:
()
=>
thi
s
.
handleError
(),
errorCallback
:
()
=>
Serverles
s
.
handleError
(),
});
});
if
(
!
Visibility
.
hidden
())
{
if
(
!
Visibility
.
hidden
())
{
...
@@ -64,7 +106,7 @@ export default class Serverless {
...
@@ -64,7 +106,7 @@ export default class Serverless {
this
.
service
this
.
service
.
fetchData
()
.
fetchData
()
.
then
(
data
=>
this
.
handleSuccess
(
data
))
.
then
(
data
=>
this
.
handleSuccess
(
data
))
.
catch
(()
=>
thi
s
.
handleError
());
.
catch
(()
=>
Serverles
s
.
handleError
());
}
}
Visibility
.
change
(()
=>
{
Visibility
.
change
(()
=>
{
...
@@ -102,5 +144,6 @@ export default class Serverless {
...
@@ -102,5 +144,6 @@ export default class Serverless {
}
}
this
.
functions
.
$destroy
();
this
.
functions
.
$destroy
();
this
.
functionDetails
.
$destroy
();
}
}
}
}
app/assets/javascripts/serverless/stores/serverless_details_store.js
0 → 100644
View file @
1102deb0
export
default
class
ServerlessDetailsStore
{
constructor
()
{
this
.
state
=
{
functionDetail
:
{},
};
}
updateDetailedFunction
(
func
)
{
this
.
state
.
functionDetail
=
func
;
}
}
app/controllers/projects/serverless/functions_controller.rb
View file @
1102deb0
...
@@ -7,19 +7,17 @@ module Projects
...
@@ -7,19 +7,17 @@ module Projects
before_action
:authorize_read_cluster!
before_action
:authorize_read_cluster!
INDEX_PRIMING_INTERVAL
=
1
0
_000
INDEX_PRIMING_INTERVAL
=
1
5
_000
INDEX_POLLING_INTERVAL
=
3
0_000
INDEX_POLLING_INTERVAL
=
6
0_000
def
index
def
index
finder
=
Projects
::
Serverless
::
FunctionsFinder
.
new
(
project
.
clusters
)
respond_to
do
|
format
|
respond_to
do
|
format
|
format
.
json
do
format
.
json
do
functions
=
finder
.
execute
functions
=
finder
.
execute
if
functions
.
any?
if
functions
.
any?
Gitlab
::
PollingInterval
.
set_header
(
response
,
interval:
INDEX_POLLING_INTERVAL
)
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
else
Gitlab
::
PollingInterval
.
set_header
(
response
,
interval:
INDEX_PRIMING_INTERVAL
)
Gitlab
::
PollingInterval
.
set_header
(
response
,
interval:
INDEX_PRIMING_INTERVAL
)
head
:no_content
head
:no_content
...
@@ -32,6 +30,29 @@ module Projects
...
@@ -32,6 +30,29 @@ module Projects
end
end
end
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
end
end
end
app/finders/projects/serverless/functions_finder.rb
View file @
1102deb0
...
@@ -15,11 +15,40 @@ module Projects
...
@@ -15,11 +15,40 @@ module Projects
clusters_with_knative_installed
.
exists?
clusters_with_knative_installed
.
exists?
end
end
def
service
(
environment_scope
,
name
)
knative_service
(
environment_scope
,
name
)
&
.
first
end
private
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
def
knative_services
clusters_with_knative_installed
.
preload_knative
.
map
do
|
cluster
|
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
end
end
...
...
app/models/clusters/applications/knative.rb
View file @
1102deb0
...
@@ -41,6 +41,8 @@ module Clusters
...
@@ -41,6 +41,8 @@ module Clusters
scope
:for_cluster
,
->
(
cluster
)
{
where
(
cluster:
cluster
)
}
scope
:for_cluster
,
->
(
cluster
)
{
where
(
cluster:
cluster
)
}
after_save
:clear_reactive_cache!
def
chart
def
chart
'knative/knative'
'knative/knative'
end
end
...
@@ -79,7 +81,7 @@ module Clusters
...
@@ -79,7 +81,7 @@ module Clusters
end
end
def
calculate_reactive_cache
def
calculate_reactive_cache
{
services:
read_services
}
{
services:
read_services
,
pods:
read_pods
}
end
end
def
ingress_service
def
ingress_service
...
@@ -87,7 +89,7 @@ module Clusters
...
@@ -87,7 +89,7 @@ module Clusters
end
end
def
services_for
(
ns:
namespace
)
def
services_for
(
ns:
namespace
)
return
unless
services
return
[]
unless
services
return
[]
unless
ns
return
[]
unless
ns
services
.
select
do
|
service
|
services
.
select
do
|
service
|
...
@@ -95,8 +97,22 @@ module Clusters
...
@@ -95,8 +97,22 @@ module Clusters
end
end
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
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
def
read_services
client
.
get_services
.
as_json
client
.
get_services
.
as_json
rescue
Kubeclient
::
ResourceNotFoundError
rescue
Kubeclient
::
ResourceNotFoundError
...
...
app/serializers/projects/serverless/service_entity.rb
View file @
1102deb0
...
@@ -13,6 +13,25 @@ module Projects
...
@@ -13,6 +13,25 @@ module Projects
service
.
dig
(
'metadata'
,
'namespace'
)
service
.
dig
(
'metadata'
,
'namespace'
)
end
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
|
expose
:created_at
do
|
service
|
service
.
dig
(
'metadata'
,
'creationTimestamp'
)
service
.
dig
(
'metadata'
,
'creationTimestamp'
)
end
end
...
@@ -22,11 +41,24 @@ module Projects
...
@@ -22,11 +41,24 @@ module Projects
end
end
expose
:description
do
|
service
|
expose
:description
do
|
service
|
service
.
dig
(
'spec'
,
'runLatest'
,
'configuration'
,
'revisionTemplate'
,
'metadata'
,
'annotations'
,
'Description'
)
service
.
dig
(
'spec'
,
'runLatest'
,
'configuration'
,
'revisionTemplate'
,
'metadata'
,
'annotations'
,
'Description'
)
end
end
expose
:image
do
|
service
|
expose
:image
do
|
service
|
service
.
dig
(
'spec'
,
'runLatest'
,
'configuration'
,
'build'
,
'template'
,
'name'
)
service
.
dig
(
'spec'
,
'runLatest'
,
'configuration'
,
'build'
,
'template'
,
'name'
)
end
end
end
end
end
end
...
...
app/views/projects/serverless/functions/show.html.haml
0 → 100644
View file @
1102deb0
-
@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
changelogs/unreleased/knative-show-page.yml
0 → 100644
View file @
1102deb0
---
title
:
Add Knative detailed view
merge_request
:
23863
author
:
Chris Baumbauer
type
:
added
config/routes/project.rb
View file @
1102deb0
...
@@ -247,6 +247,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
...
@@ -247,6 +247,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
namespace
:serverless
do
namespace
:serverless
do
get
'/functions/:environment_id/:id'
,
to:
'functions#show'
resources
:functions
,
only:
[
:index
]
resources
:functions
,
only:
[
:index
]
end
end
...
...
doc/user/project/clusters/serverless/img/serverless-details.png
0 → 100644
View file @
1102deb0
61.9 KB
doc/user/project/clusters/serverless/img/serverless-page.png
View replaced file @
71026ffd
View file @
1102deb0
190 KB
|
W:
|
H:
60.9 KB
|
W:
|
H:
2-up
Swipe
Onion skin
doc/user/project/clusters/serverless/index.md
View file @
1102deb0
...
@@ -167,8 +167,8 @@ appear under **Operations > Serverless**.
...
@@ -167,8 +167,8 @@ appear under **Operations > Serverless**.
![
serverless page
](
img/serverless-page.png
)
![
serverless page
](
img/serverless-page.png
)
This page contains all functions available for the project, the
URL
for
This page contains all functions available for the project, the
description
for
accessing the function, and if available, the function's runtime information.
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
The details are derived from the Knative installation inside each of the project's
Kubernetes cluster.
Kubernetes cluster.
...
@@ -184,6 +184,12 @@ The sample function can now be triggered from any HTTP client using a simple `PO
...
@@ -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.
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
## Deploying Serverless applications
> Introduced in GitLab 11.5.
> Introduced in GitLab 11.5.
...
...
locale/gitlab.pot
View file @
1102deb0
...
@@ -6057,13 +6057,31 @@ msgstr ""
...
@@ -6057,13 +6057,31 @@ msgstr ""
msgid "Serverless"
msgid "Serverless"
msgstr ""
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."
msgid "Serverless| In order to start using functions as a service, you must first install Knative on your Kubernetes cluster."
msgstr ""
msgstr ""
msgid "Serverless|An error occurred while retrieving serverless components"
msgid "Serverless|An error occurred while retrieving serverless components"
msgstr ""
msgstr ""
msgid "Serverless|Domain"
msgid "Serverless|Cluster Env"
msgstr ""
msgid "Serverless|Description"
msgstr ""
msgstr ""
msgid "Serverless|Function"
msgid "Serverless|Function"
...
...
spec/controllers/projects/serverless/functions_controller_spec.rb
View file @
1102deb0
...
@@ -45,9 +45,45 @@ describe Projects::Serverless::FunctionsController do
...
@@ -45,9 +45,45 @@ describe Projects::Serverless::FunctionsController do
end
end
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
describe
'GET #index with data'
,
:use_clean_rails_memory_store_caching
do
before
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
end
it
'has data'
do
it
'has data'
do
...
...
spec/finders/projects/serverless/functions_finder_spec.rb
View file @
1102deb0
...
@@ -29,15 +29,34 @@ describe Projects::Serverless::FunctionsFinder do
...
@@ -29,15 +29,34 @@ describe Projects::Serverless::FunctionsFinder do
context
'has knative installed'
do
context
'has knative installed'
do
let!
(
:knative
)
{
create
(
:clusters_applications_knative
,
:installed
,
cluster:
cluster
)
}
let!
(
:knative
)
{
create
(
:clusters_applications_knative
,
:installed
,
cluster:
cluster
)
}
let
(
:finder
)
{
described_class
.
new
(
project
.
clusters
)
}
it
'there are no functions'
do
it
'there are no functions'
do
expect
(
described_class
.
new
(
project
.
clusters
)
.
execute
).
to
be_empty
expect
(
finder
.
execute
).
to
be_empty
end
end
it
'there are functions'
,
:use_clean_rails_memory_store_caching
do
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
(
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"
]
})
expect
(
described_class
.
new
(
project
.
clusters
).
execute
).
not_to
be_empty
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
end
end
end
...
...
spec/models/clusters/applications/knative_spec.rb
View file @
1102deb0
...
@@ -149,6 +149,35 @@ describe Clusters::Applications::Knative do
...
@@ -149,6 +149,35 @@ describe Clusters::Applications::Knative do
it
{
is_expected
.
to
validate_presence_of
(
:hostname
)
}
it
{
is_expected
.
to
validate_presence_of
(
:hostname
)
}
end
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
describe
'#services'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:service
)
{
cluster
.
platform_kubernetes
}
let
(
:service
)
{
cluster
.
platform_kubernetes
}
...
@@ -166,6 +195,7 @@ describe Clusters::Applications::Knative do
...
@@ -166,6 +195,7 @@ describe Clusters::Applications::Knative do
before
do
before
do
stub_kubeclient_discover
(
service
.
api_url
)
stub_kubeclient_discover
(
service
.
api_url
)
stub_kubeclient_knative_services
stub_kubeclient_knative_services
stub_kubeclient_service_pods
end
end
it
'should have an unintialized cache'
do
it
'should have an unintialized cache'
do
...
@@ -174,7 +204,11 @@ describe Clusters::Applications::Knative do
...
@@ -174,7 +204,11 @@ describe Clusters::Applications::Knative do
context
'when using synchronous reactive cache'
do
context
'when using synchronous reactive cache'
do
before
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
)
synchronous_reactive_cache
(
knative
)
end
end
...
...
spec/support/helpers/kubernetes_helpers.rb
View file @
1102deb0
...
@@ -20,6 +20,13 @@ module KubernetesHelpers
...
@@ -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
))
WebMock
.
stub_request
(
:get
,
api_url
+
'/apis/serving.knative.dev/v1alpha1'
).
to_return
(
kube_response
(
kube_v1alpha1_serving_knative_discovery_body
))
end
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
)
def
stub_kubeclient_pods
(
response
=
nil
)
stub_kubeclient_discover
(
service
.
api_url
)
stub_kubeclient_discover
(
service
.
api_url
)
pods_url
=
service
.
api_url
+
"/api/v1/namespaces/
#{
service
.
actual_namespace
}
/pods"
pods_url
=
service
.
api_url
+
"/api/v1/namespaces/
#{
service
.
actual_namespace
}
/pods"
...
@@ -212,6 +219,13 @@ module KubernetesHelpers
...
@@ -212,6 +219,13 @@ module KubernetesHelpers
}
}
end
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
)
def
kube_knative_services_body
(
**
options
)
{
{
"kind"
=>
"List"
,
"kind"
=>
"List"
,
...
@@ -242,6 +256,28 @@ module KubernetesHelpers
...
@@ -242,6 +256,28 @@ module KubernetesHelpers
}
}
end
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
)
def
kube_deployment
(
name:
"kube-deployment"
,
app:
"valid-deployment-label"
,
track:
nil
)
{
{
"metadata"
=>
{
"metadata"
=>
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment