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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
2c80a1c0
Commit
2c80a1c0
authored
Dec 06, 2018
by
Dylan Griffith
Committed by
Mike Greiling
Dec 06, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Introduce Knative Serverless Tab
parent
e80f8933
Changes
27
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
866 additions
and
2 deletions
+866
-2
app/assets/javascripts/pages/projects/serverless/index.js
app/assets/javascripts/pages/projects/serverless/index.js
+5
-0
app/assets/javascripts/serverless/components/empty_state.vue
app/assets/javascripts/serverless/components/empty_state.vue
+40
-0
app/assets/javascripts/serverless/components/function_row.vue
...assets/javascripts/serverless/components/function_row.vue
+40
-0
app/assets/javascripts/serverless/components/functions.vue
app/assets/javascripts/serverless/components/functions.vue
+123
-0
app/assets/javascripts/serverless/event_hub.js
app/assets/javascripts/serverless/event_hub.js
+3
-0
app/assets/javascripts/serverless/serverless_bundle.js
app/assets/javascripts/serverless/serverless_bundle.js
+106
-0
app/assets/javascripts/serverless/services/get_functions_service.js
.../javascripts/serverless/services/get_functions_service.js
+11
-0
app/assets/javascripts/serverless/stores/serverless_store.js
app/assets/javascripts/serverless/stores/serverless_store.js
+24
-0
app/controllers/projects/serverless/functions_controller.rb
app/controllers/projects/serverless/functions_controller.rb
+37
-0
app/finders/projects/serverless/functions_finder.rb
app/finders/projects/serverless/functions_finder.rb
+31
-0
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+2
-0
app/models/clusters/applications/knative.rb
app/models/clusters/applications/knative.rb
+34
-2
app/models/clusters/cluster.rb
app/models/clusters/cluster.rb
+10
-0
app/serializers/projects/serverless/service_entity.rb
app/serializers/projects/serverless/service_entity.rb
+33
-0
app/serializers/projects/serverless/service_serializer.rb
app/serializers/projects/serverless/service_serializer.rb
+9
-0
app/views/layouts/nav/sidebar/_project.html.haml
app/views/layouts/nav/sidebar/_project.html.haml
+6
-0
app/views/projects/serverless/functions/index.html.haml
app/views/projects/serverless/functions/index.html.haml
+15
-0
changelogs/unreleased/triggermesh-phase2-serverless-list.yml
changelogs/unreleased/triggermesh-phase2-serverless-list.yml
+5
-0
config/routes/project.rb
config/routes/project.rb
+4
-0
doc/user/project/clusters/serverless/img/install-knative.png
doc/user/project/clusters/serverless/img/install-knative.png
+0
-0
doc/user/project/clusters/serverless/img/serverless-page.png
doc/user/project/clusters/serverless/img/serverless-page.png
+0
-0
locale/gitlab.pot
locale/gitlab.pot
+39
-0
spec/controllers/projects/serverless/functions_controller_spec.rb
...trollers/projects/serverless/functions_controller_spec.rb
+72
-0
spec/features/projects/serverless/functions_spec.rb
spec/features/projects/serverless/functions_spec.rb
+49
-0
spec/finders/projects/serverless/functions_finder_spec.rb
spec/finders/projects/serverless/functions_finder_spec.rb
+60
-0
spec/models/clusters/applications/knative_spec.rb
spec/models/clusters/applications/knative_spec.rb
+42
-0
spec/support/helpers/kubernetes_helpers.rb
spec/support/helpers/kubernetes_helpers.rb
+66
-0
No files found.
app/assets/javascripts/pages/projects/serverless/index.js
0 → 100644
View file @
2c80a1c0
import
ServerlessBundle
from
'
~/serverless/serverless_bundle
'
;
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
new
ServerlessBundle
();
// eslint-disable-line no-new
});
app/assets/javascripts/serverless/components/empty_state.vue
0 → 100644
View file @
2c80a1c0
<
script
>
export
default
{
props
:
{
clustersPath
:
{
type
:
String
,
required
:
true
,
},
helpPath
:
{
type
:
String
,
required
:
true
,
},
},
};
</
script
>
<
template
>
<div
class=
"row empty-state js-empty-state"
>
<div
class=
"col-12"
>
<div
class=
"text-content"
>
<h4
class=
"state-title text-center"
>
{{
s__
(
'
Serverless|Getting started with serverless
'
)
}}
</h4>
<p
class=
"state-description"
>
{{
s__
(
`Serverless| In order to start using functions as a service,
you must first install Knative on your Kubernetes cluster.`
)
}}
<a
:href=
"helpPath"
>
{{
__
(
'
More information
'
)
}}
</a>
</p>
<div
class=
"text-center"
>
<a
:href=
"clustersPath"
class=
"btn btn-success"
>
{{
s__
(
'
Serverless|Install Knative
'
)
}}
</a>
</div>
</div>
</div>
</div>
</
template
>
app/assets/javascripts/serverless/components/function_row.vue
0 → 100644
View file @
2c80a1c0
<
script
>
import
Timeago
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
export
default
{
components
:
{
Timeago
,
},
props
:
{
func
:
{
type
:
Object
,
required
:
true
,
},
},
computed
:
{
name
()
{
return
this
.
func
.
name
;
},
url
()
{
return
this
.
func
.
url
;
},
image
()
{
return
this
.
func
.
image
;
},
timestamp
()
{
return
this
.
func
.
created_at
;
},
},
};
</
script
>
<
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>
<div
class=
"table-section section-20"
>
{{
image
}}
</div>
<div
class=
"table-section section-10"
><timeago
:time=
"timestamp"
/></div>
</div>
</
template
>
app/assets/javascripts/serverless/components/functions.vue
0 → 100644
View file @
2c80a1c0
<
script
>
import
{
GlSkeletonLoading
}
from
'
@gitlab/ui
'
;
import
FunctionRow
from
'
./function_row.vue
'
;
import
EmptyState
from
'
./empty_state.vue
'
;
export
default
{
components
:
{
FunctionRow
,
EmptyState
,
GlSkeletonLoading
,
},
props
:
{
functions
:
{
type
:
Array
,
required
:
true
,
default
:
()
=>
[],
},
installed
:
{
type
:
Boolean
,
required
:
true
,
},
clustersPath
:
{
type
:
String
,
required
:
true
,
},
helpPath
:
{
type
:
String
,
required
:
true
,
},
loadingData
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
hasFunctionData
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
},
};
</
script
>
<
template
>
<section
id=
"serverless-functions"
>
<div
v-if=
"installed"
>
<div
v-if=
"hasFunctionData"
>
<div
class=
"ci-table js-services-list function-element"
>
<div
class=
"gl-responsive-table-row table-row-header"
role=
"row"
>
<div
class=
"table-section section-20"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Function
'
)
}}
</div>
<div
class=
"table-section section-50"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Domain
'
)
}}
</div>
<div
class=
"table-section section-20"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Runtime
'
)
}}
</div>
<div
class=
"table-section section-10"
role=
"rowheader"
>
{{
s__
(
'
Serverless|Last Update
'
)
}}
</div>
</div>
<template
v-if=
"loadingData"
>
<div
v-for=
"j in 3"
:key=
"j"
class=
"gl-responsive-table-row"
>
<gl-skeleton-loading
/>
</div>
</
template
>
<
template
v-else
>
<function-row
v-for=
"f in functions"
:key=
"f.name"
:func=
"f"
/>
</
template
>
</div>
</div>
<div
v-else
class=
"empty-state js-empty-state"
>
<div
class=
"text-content"
>
<h4
class=
"state-title text-center"
>
{{ s__('Serverless|No functions available') }}
</h4>
<p
class=
"state-description"
>
{{
s__(`Serverless|There is currently no function data available from Knative.
This could be for a variety of reasons including:`)
}}
</p>
<ul>
<li>
Your repository does not have a corresponding
<code>
serverless.yml
</code>
file.
</li>
<li>
Your
<code>
gitlab-ci.yml
</code>
file is not properly configured.
</li>
<li>
The functions listed in the
<code>
serverless.yml
</code>
file don't match the namespace
of your cluster.
</li>
<li>
The deploy job has not finished.
</li>
</ul>
<p>
{{
s__(`Serverless|If you believe none of these apply, please check
back later as the function data may be in the process of becoming
available.`)
}}
</p>
<div
class=
"text-center"
>
<a
:href=
"helpPath"
class=
"btn btn-success"
>
{{ s__('Serverless|Learn more about Serverless') }}
</a>
</div>
</div>
</div>
</div>
<empty-state
v-else
:clusters-path=
"clustersPath"
:help-path=
"helpPath"
/>
</section>
</template>
<
style
>
.top-area
{
border-bottom
:
0
;
}
.function-element
{
border-bottom
:
1px
solid
#e5e5e5
;
border-bottom-color
:
rgb
(
229
,
229
,
229
);
border-bottom-style
:
solid
;
border-bottom-width
:
1px
;
}
</
style
>
app/assets/javascripts/serverless/event_hub.js
0 → 100644
View file @
2c80a1c0
import
Vue
from
'
vue
'
;
export
default
new
Vue
();
app/assets/javascripts/serverless/serverless_bundle.js
0 → 100644
View file @
2c80a1c0
import
Visibility
from
'
visibilityjs
'
;
import
Vue
from
'
vue
'
;
import
{
s__
}
from
'
../locale
'
;
import
Flash
from
'
../flash
'
;
import
Poll
from
'
../lib/utils/poll
'
;
import
ServerlessStore
from
'
./stores/serverless_store
'
;
import
GetFunctionsService
from
'
./services/get_functions_service
'
;
import
Functions
from
'
./components/functions.vue
'
;
export
default
class
Serverless
{
constructor
()
{
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
();
}
}
initServerless
()
{
const
{
store
}
=
this
;
const
el
=
document
.
querySelector
(
'
#js-serverless-functions
'
);
this
.
functions
=
new
Vue
({
el
,
data
()
{
return
{
state
:
store
.
state
,
};
},
render
(
createElement
)
{
return
createElement
(
Functions
,
{
props
:
{
functions
:
this
.
state
.
functions
,
installed
:
this
.
state
.
installed
,
clustersPath
:
this
.
state
.
clustersPath
,
helpPath
:
this
.
state
.
helpPath
,
loadingData
:
this
.
state
.
loadingData
,
hasFunctionData
:
this
.
state
.
hasFunctionData
,
},
});
},
});
}
initPolling
()
{
this
.
poll
=
new
Poll
({
resource
:
this
.
service
,
method
:
'
fetchData
'
,
successCallback
:
data
=>
this
.
handleSuccess
(
data
),
errorCallback
:
()
=>
this
.
handleError
(),
});
if
(
!
Visibility
.
hidden
())
{
this
.
poll
.
makeRequest
();
}
else
{
this
.
service
.
fetchData
()
.
then
(
data
=>
this
.
handleSuccess
(
data
))
.
catch
(()
=>
this
.
handleError
());
}
Visibility
.
change
(()
=>
{
if
(
!
Visibility
.
hidden
()
&&
!
this
.
destroyed
)
{
this
.
poll
.
restart
();
}
else
{
this
.
poll
.
stop
();
}
});
}
handleSuccess
(
data
)
{
if
(
data
.
status
===
200
)
{
this
.
store
.
updateFunctionsFromServer
(
data
.
data
);
this
.
store
.
updateLoadingState
(
false
);
}
else
if
(
data
.
status
===
204
)
{
/* Time out after 3 attempts to retrieve data */
this
.
functionLoadCount
+=
1
;
if
(
this
.
functionLoadCount
===
3
)
{
this
.
poll
.
stop
();
this
.
store
.
toggleNoFunctionData
();
}
}
}
static
handleError
()
{
Flash
(
s__
(
'
Serverless|An error occurred while retrieving serverless components
'
));
}
destroy
()
{
this
.
destroyed
=
true
;
if
(
this
.
poll
)
{
this
.
poll
.
stop
();
}
this
.
functions
.
$destroy
();
}
}
app/assets/javascripts/serverless/services/get_functions_service.js
0 → 100644
View file @
2c80a1c0
import
axios
from
'
~/lib/utils/axios_utils
'
;
export
default
class
GetFunctionsService
{
constructor
(
endpoint
)
{
this
.
endpoint
=
endpoint
;
}
fetchData
()
{
return
axios
.
get
(
this
.
endpoint
);
}
}
app/assets/javascripts/serverless/stores/serverless_store.js
0 → 100644
View file @
2c80a1c0
export
default
class
ServerlessStore
{
constructor
(
knativeInstalled
=
false
,
clustersPath
,
helpPath
)
{
this
.
state
=
{
functions
:
[],
hasFunctionData
:
true
,
loadingData
:
true
,
installed
:
knativeInstalled
,
clustersPath
,
helpPath
,
};
}
updateFunctionsFromServer
(
functions
=
[])
{
this
.
state
.
functions
=
functions
;
}
updateLoadingState
(
loadingData
)
{
this
.
state
.
loadingData
=
loadingData
;
}
toggleNoFunctionData
()
{
this
.
state
.
hasFunctionData
=
false
;
}
}
app/controllers/projects/serverless/functions_controller.rb
0 → 100644
View file @
2c80a1c0
# frozen_string_literal: true
module
Projects
module
Serverless
class
FunctionsController
<
Projects
::
ApplicationController
include
ProjectUnauthorized
before_action
:authorize_read_cluster!
INDEX_PRIMING_INTERVAL
=
10_000
INDEX_POLLING_INTERVAL
=
30_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
)
else
Gitlab
::
PollingInterval
.
set_header
(
response
,
interval:
INDEX_PRIMING_INTERVAL
)
head
:no_content
end
end
format
.
html
do
@installed
=
finder
.
installed?
render
end
end
end
end
end
end
app/finders/projects/serverless/functions_finder.rb
0 → 100644
View file @
2c80a1c0
# frozen_string_literal: true
module
Projects
module
Serverless
class
FunctionsFinder
def
initialize
(
clusters
)
@clusters
=
clusters
end
def
execute
knative_services
.
flatten
.
compact
end
def
installed?
clusters_with_knative_installed
.
exists?
end
private
def
knative_services
clusters_with_knative_installed
.
preload_knative
.
map
do
|
cluster
|
cluster
.
application_knative
.
services_for
(
ns:
cluster
.
platform_kubernetes
&
.
actual_namespace
)
end
end
def
clusters_with_knative_installed
@clusters
.
with_knative_installed
end
end
end
end
app/helpers/projects_helper.rb
View file @
2c80a1c0
...
@@ -307,6 +307,7 @@ module ProjectsHelper
...
@@ -307,6 +307,7 @@ module ProjectsHelper
settings: :admin_project
,
settings: :admin_project
,
builds: :read_build
,
builds: :read_build
,
clusters: :read_cluster
,
clusters: :read_cluster
,
serverless: :read_cluster
,
labels: :read_label
,
labels: :read_label
,
issues: :read_issue
,
issues: :read_issue
,
project_members: :read_project_member
,
project_members: :read_project_member
,
...
@@ -545,6 +546,7 @@ module ProjectsHelper
...
@@ -545,6 +546,7 @@ module ProjectsHelper
%w[
%w[
environments
environments
clusters
clusters
functions
user
user
gcp
gcp
]
]
...
...
app/models/clusters/applications/knative.rb
View file @
2c80a1c0
...
@@ -15,6 +15,9 @@ module Clusters
...
@@ -15,6 +15,9 @@ module Clusters
include
::
Clusters
::
Concerns
::
ApplicationVersion
include
::
Clusters
::
Concerns
::
ApplicationVersion
include
::
Clusters
::
Concerns
::
ApplicationData
include
::
Clusters
::
Concerns
::
ApplicationData
include
AfterCommitQueue
include
AfterCommitQueue
include
ReactiveCaching
self
.
reactive_cache_key
=
->
(
knative
)
{
[
knative
.
class
.
model_name
.
singular
,
knative
.
id
]
}
state_machine
:status
do
state_machine
:status
do
before_transition
any
=>
[
:installed
]
do
|
application
|
before_transition
any
=>
[
:installed
]
do
|
application
|
...
@@ -29,6 +32,8 @@ module Clusters
...
@@ -29,6 +32,8 @@ module Clusters
validates
:hostname
,
presence:
true
,
hostname:
true
validates
:hostname
,
presence:
true
,
hostname:
true
scope
:for_cluster
,
->
(
cluster
)
{
where
(
cluster:
cluster
)
}
def
chart
def
chart
'knative/knative'
'knative/knative'
end
end
...
@@ -55,12 +60,39 @@ module Clusters
...
@@ -55,12 +60,39 @@ module Clusters
ClusterWaitForIngressIpAddressWorker
.
perform_async
(
name
,
id
)
ClusterWaitForIngressIpAddressWorker
.
perform_async
(
name
,
id
)
end
end
def
client
cluster
.
kubeclient
.
knative_client
end
def
services
with_reactive_cache
do
|
data
|
data
[
:services
]
end
end
def
calculate_reactive_cache
{
services:
read_services
}
end
def
ingress_service
def
ingress_service
cluster
.
kubeclient
.
get_service
(
'knative-ingressgateway'
,
'istio-system'
)
cluster
.
kubeclient
.
get_service
(
'knative-ingressgateway'
,
'istio-system'
)
end
end
def
client
def
services_for
(
ns:
namespace
)
cluster
.
platform_kubernetes
.
kubeclient
.
knative_client
return
unless
services
return
[]
unless
ns
services
.
select
do
|
service
|
service
.
dig
(
'metadata'
,
'namespace'
)
==
ns
end
end
private
def
read_services
client
.
get_services
.
as_json
rescue
Kubeclient
::
ResourceNotFoundError
[]
end
end
end
end
end
end
...
...
app/models/clusters/cluster.rb
View file @
2c80a1c0
...
@@ -93,6 +93,16 @@ module Clusters
...
@@ -93,6 +93,16 @@ module Clusters
where
(
'NOT EXISTS (?)'
,
subquery
)
where
(
'NOT EXISTS (?)'
,
subquery
)
end
end
scope
:with_knative_installed
,
->
{
joins
(
:application_knative
).
merge
(
Clusters
::
Applications
::
Knative
.
installed
)
}
scope
:preload_knative
,
->
{
preload
(
:kubernetes_namespace
,
:platform_kubernetes
,
:application_knative
)
}
def
self
.
ancestor_clusters_for_clusterable
(
clusterable
,
hierarchy_order: :asc
)
def
self
.
ancestor_clusters_for_clusterable
(
clusterable
,
hierarchy_order: :asc
)
hierarchy_groups
=
clusterable
.
ancestors_upto
(
hierarchy_order:
hierarchy_order
).
eager_load
(
:clusters
)
hierarchy_groups
=
clusterable
.
ancestors_upto
(
hierarchy_order:
hierarchy_order
).
eager_load
(
:clusters
)
hierarchy_groups
=
hierarchy_groups
.
merge
(
current_scope
)
if
current_scope
hierarchy_groups
=
hierarchy_groups
.
merge
(
current_scope
)
if
current_scope
...
...
app/serializers/projects/serverless/service_entity.rb
0 → 100644
View file @
2c80a1c0
# frozen_string_literal: true
module
Projects
module
Serverless
class
ServiceEntity
<
Grape
::
Entity
include
RequestAwareEntity
expose
:name
do
|
service
|
service
.
dig
(
'metadata'
,
'name'
)
end
expose
:namespace
do
|
service
|
service
.
dig
(
'metadata'
,
'namespace'
)
end
expose
:created_at
do
|
service
|
service
.
dig
(
'metadata'
,
'creationTimestamp'
)
end
expose
:url
do
|
service
|
"http://
#{
service
.
dig
(
'status'
,
'domain'
)
}
"
end
expose
:description
do
|
service
|
service
.
dig
(
'spec'
,
'runLatest'
,
'configuration'
,
'revisionTemplate'
,
'metadata'
,
'annotations'
,
'Description'
)
end
expose
:image
do
|
service
|
service
.
dig
(
'spec'
,
'runLatest'
,
'configuration'
,
'build'
,
'template'
,
'name'
)
end
end
end
end
app/serializers/projects/serverless/service_serializer.rb
0 → 100644
View file @
2c80a1c0
# frozen_string_literal: true
module
Projects
module
Serverless
class
ServiceSerializer
<
BaseSerializer
entity
Projects
::
Serverless
::
ServiceEntity
end
end
end
app/views/layouts/nav/sidebar/_project.html.haml
View file @
2c80a1c0
...
@@ -222,6 +222,12 @@
...
@@ -222,6 +222,12 @@
%span
%span
=
_
(
'Environments'
)
=
_
(
'Environments'
)
-
if
project_nav_tab?
:serverless
=
nav_link
(
controller: :functions
)
do
=
link_to
project_serverless_functions_path
(
@project
),
title:
_
(
'Serverless'
)
do
%span
=
_
(
'Serverless'
)
-
if
project_nav_tab?
:clusters
-
if
project_nav_tab?
:clusters
-
show_cluster_hint
=
show_gke_cluster_integration_callout?
(
@project
)
-
show_cluster_hint
=
show_gke_cluster_integration_callout?
(
@project
)
=
nav_link
(
controller:
[
:clusters
,
:user
,
:gcp
])
do
=
nav_link
(
controller:
[
:clusters
,
:user
,
:gcp
])
do
...
...
app/views/projects/serverless/functions/index.html.haml
0 → 100644
View file @
2c80a1c0
-
@no_container
=
true
-
@content_class
=
"limit-container-width"
unless
fluid_layout
-
breadcrumb_title
'Serverless'
-
page_title
'Serverless'
-
status_path
=
project_serverless_functions_path
(
@project
,
format: :json
)
-
clusters_path
=
project_clusters_path
(
@project
)
.serverless-functions-page.js-serverless-functions-page
{
data:
{
status_path:
status_path
,
installed:
@installed
,
clusters_path:
clusters_path
,
help_path:
help_page_path
(
'user/project/clusters/serverless/index'
)
}
}
%div
{
class:
[
container_class
,
(
'limit-container-width'
unless
fluid_layout
)]
}
.js-serverless-functions-notice
.flash-container
.top-area.adjust
.serverless-functions-table
#js-serverless-functions
changelogs/unreleased/triggermesh-phase2-serverless-list.yml
0 → 100644
View file @
2c80a1c0
---
title
:
Introduce Knative and Serverless Components
merge_request
:
23174
author
:
Chris Baumbauer
type
:
added
config/routes/project.rb
View file @
2c80a1c0
...
@@ -245,6 +245,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
...
@@ -245,6 +245,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
end
end
namespace
:serverless
do
resources
:functions
,
only:
[
:index
]
end
scope
'-'
do
scope
'-'
do
get
'archive/*id'
,
constraints:
{
format:
Gitlab
::
PathRegex
.
archive_formats_regex
,
id:
/.+?/
},
to:
'repositories#archive'
,
as:
'archive'
get
'archive/*id'
,
constraints:
{
format:
Gitlab
::
PathRegex
.
archive_formats_regex
,
id:
/.+?/
},
to:
'repositories#archive'
,
as:
'archive'
...
...
doc/user/project/clusters/serverless/img/install-knative.png
View replaced file @
e80f8933
View file @
2c80a1c0
100 KB
|
W:
|
H:
30.5 KB
|
W:
|
H:
2-up
Swipe
Onion skin
doc/user/project/clusters/serverless/img/serverless-page.png
0 → 100644
View file @
2c80a1c0
31 KB
locale/gitlab.pot
View file @
2c80a1c0
...
@@ -5815,6 +5815,45 @@ msgstr ""
...
@@ -5815,6 +5815,45 @@ msgstr ""
msgid "Server version"
msgid "Server version"
msgstr ""
msgstr ""
msgid "Serverless"
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"
msgstr ""
msgid "Serverless|Function"
msgstr ""
msgid "Serverless|Getting started with serverless"
msgstr ""
msgid "Serverless|If you believe none of these apply, please check back later as the function data may be in the process of becoming available."
msgstr ""
msgid "Serverless|Install Knative"
msgstr ""
msgid "Serverless|Last Update"
msgstr ""
msgid "Serverless|Learn more about Serverless"
msgstr ""
msgid "Serverless|No functions available"
msgstr ""
msgid "Serverless|Runtime"
msgstr ""
msgid "Serverless|There is currently no function data available from Knative. This could be for a variety of reasons including:"
msgstr ""
msgid "Service Templates"
msgid "Service Templates"
msgstr ""
msgstr ""
...
...
spec/controllers/projects/serverless/functions_controller_spec.rb
0 → 100644
View file @
2c80a1c0
# frozen_string_literal: true
require
'spec_helper'
describe
Projects
::
Serverless
::
FunctionsController
do
include
KubernetesHelpers
include
ReactiveCachingHelpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:knative
)
{
create
(
:clusters_applications_knative
,
:installed
,
cluster:
cluster
)
}
let
(
:service
)
{
cluster
.
platform_kubernetes
}
let
(
:project
)
{
cluster
.
project
}
let
(
:namespace
)
do
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster
,
cluster_project:
cluster
.
cluster_project
,
project:
cluster
.
cluster_project
.
project
)
end
before
do
project
.
add_maintainer
(
user
)
sign_in
(
user
)
end
def
params
(
opts
=
{})
opts
.
reverse_merge
(
namespace_id:
project
.
namespace
.
to_param
,
project_id:
project
.
to_param
)
end
describe
'GET #index'
do
context
'empty cache'
do
it
'has no data'
do
get
:index
,
params
({
format: :json
})
expect
(
response
).
to
have_gitlab_http_status
(
204
)
end
it
'renders an html page'
do
get
:index
,
params
expect
(
response
).
to
have_gitlab_http_status
(
200
)
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"
])
end
it
'has data'
do
get
:index
,
params
({
format: :json
})
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
json_response
).
to
contain_exactly
(
a_hash_including
(
"name"
=>
project
.
name
,
"url"
=>
"http://
#{
project
.
name
}
.
#{
namespace
.
namespace
}
.example.com"
)
)
end
it
'has data in html'
do
get
:index
,
params
expect
(
response
).
to
have_gitlab_http_status
(
200
)
end
end
end
spec/features/projects/serverless/functions_spec.rb
0 → 100644
View file @
2c80a1c0
require
'spec_helper'
describe
'Functions'
,
:js
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:user
)
{
create
(
:user
)
}
before
do
project
.
add_maintainer
(
user
)
gitlab_sign_in
(
user
)
end
context
'when user does not have a cluster and visits the serverless page'
do
before
do
visit
project_serverless_functions_path
(
project
)
end
it
'sees an empty state'
do
expect
(
page
).
to
have_link
(
'Install Knative'
)
expect
(
page
).
to
have_selector
(
'.empty-state'
)
end
end
context
'when the user does have a cluster and visits the serverless page'
do
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
before
do
visit
project_serverless_functions_path
(
project
)
end
it
'sees an empty state'
do
expect
(
page
).
to
have_link
(
'Install Knative'
)
expect
(
page
).
to
have_selector
(
'.empty-state'
)
end
end
context
'when the user has a cluster and knative installed and visits the serverless page'
do
let!
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:knative
)
{
create
(
:clusters_applications_knative
,
:installed
,
cluster:
cluster
)
}
let
(
:project
)
{
knative
.
cluster
.
project
}
before
do
visit
project_serverless_functions_path
(
project
)
end
it
'sees an empty listing of serverless functions'
do
expect
(
page
).
to
have_selector
(
'.gl-responsive-table-row'
)
end
end
end
spec/finders/projects/serverless/functions_finder_spec.rb
0 → 100644
View file @
2c80a1c0
# frozen_string_literal: true
require
'spec_helper'
describe
Projects
::
Serverless
::
FunctionsFinder
do
include
KubernetesHelpers
include
ReactiveCachingHelpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:cluster
)
{
create
(
:cluster
,
:project
,
:provided_by_gcp
)
}
let
(
:service
)
{
cluster
.
platform_kubernetes
}
let
(
:project
)
{
cluster
.
project
}
let
(
:namespace
)
do
create
(
:cluster_kubernetes_namespace
,
cluster:
cluster
,
cluster_project:
cluster
.
cluster_project
,
project:
cluster
.
cluster_project
.
project
)
end
before
do
project
.
add_maintainer
(
user
)
end
describe
'retrieve data from knative'
do
it
'does not have knative installed'
do
expect
(
described_class
.
new
(
project
.
clusters
).
execute
).
to
be_empty
end
context
'has knative installed'
do
let!
(
:knative
)
{
create
(
:clusters_applications_knative
,
:installed
,
cluster:
cluster
)
}
it
'there are no functions'
do
expect
(
described_class
.
new
(
project
.
clusters
).
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"
])
expect
(
described_class
.
new
(
project
.
clusters
).
execute
).
not_to
be_empty
end
end
end
describe
'verify if knative is installed'
do
context
'knative is not installed'
do
it
'does not have knative installed'
do
expect
(
described_class
.
new
(
project
.
clusters
).
installed?
).
to
be
false
end
end
context
'knative is installed'
do
let!
(
:knative
)
{
create
(
:clusters_applications_knative
,
:installed
,
cluster:
cluster
)
}
it
'does have knative installed'
do
expect
(
described_class
.
new
(
project
.
clusters
).
installed?
).
to
be
true
end
end
end
end
spec/models/clusters/applications/knative_spec.rb
View file @
2c80a1c0
require
'rails_helper'
require
'rails_helper'
describe
Clusters
::
Applications
::
Knative
do
describe
Clusters
::
Applications
::
Knative
do
include
KubernetesHelpers
include
ReactiveCachingHelpers
let
(
:knative
)
{
create
(
:clusters_applications_knative
)
}
let
(
:knative
)
{
create
(
:clusters_applications_knative
)
}
include_examples
'cluster application core specs'
,
:clusters_applications_knative
include_examples
'cluster application core specs'
,
:clusters_applications_knative
...
@@ -121,4 +124,43 @@ describe Clusters::Applications::Knative do
...
@@ -121,4 +124,43 @@ describe Clusters::Applications::Knative do
describe
'validations'
do
describe
'validations'
do
it
{
is_expected
.
to
validate_presence_of
(
:hostname
)
}
it
{
is_expected
.
to
validate_presence_of
(
:hostname
)
}
end
end
describe
'#services'
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
subject
{
knative
.
services
}
before
do
stub_kubeclient_discover
(
service
.
api_url
)
stub_kubeclient_knative_services
end
it
'should have an unintialized cache'
do
is_expected
.
to
be_nil
end
context
'when using synchronous reactive cache'
do
before
do
stub_reactive_cache
(
knative
,
services:
kube_response
(
kube_knative_services_body
))
synchronous_reactive_cache
(
knative
)
end
it
'should have cached services'
do
is_expected
.
not_to
be_nil
end
it
'should match our namespace'
do
expect
(
knative
.
services_for
(
ns:
namespace
)).
not_to
be_nil
end
end
end
end
end
spec/support/helpers/kubernetes_helpers.rb
View file @
2c80a1c0
...
@@ -34,6 +34,17 @@ module KubernetesHelpers
...
@@ -34,6 +34,17 @@ module KubernetesHelpers
WebMock
.
stub_request
(
:get
,
deployments_url
).
to_return
(
response
||
kube_deployments_response
)
WebMock
.
stub_request
(
:get
,
deployments_url
).
to_return
(
response
||
kube_deployments_response
)
end
end
def
stub_kubeclient_knative_services
(
**
options
)
options
[
:name
]
||=
"kubetest"
options
[
:namespace
]
||=
"default"
options
[
:domain
]
||=
"example.com"
stub_kubeclient_discover
(
service
.
api_url
)
knative_url
=
service
.
api_url
+
"/apis/serving.knative.dev/v1alpha1/services"
WebMock
.
stub_request
(
:get
,
knative_url
).
to_return
(
kube_response
(
kube_knative_services_body
(
options
)))
end
def
stub_kubeclient_get_secret
(
api_url
,
**
options
)
def
stub_kubeclient_get_secret
(
api_url
,
**
options
)
options
[
:metadata_name
]
||=
"default-token-1"
options
[
:metadata_name
]
||=
"default-token-1"
options
[
:namespace
]
||=
"default"
options
[
:namespace
]
||=
"default"
...
@@ -181,6 +192,13 @@ module KubernetesHelpers
...
@@ -181,6 +192,13 @@ module KubernetesHelpers
}
}
end
end
def
kube_knative_services_body
(
**
options
)
{
"kind"
=>
"List"
,
"items"
=>
[
kube_service
(
options
)]
}
end
# This is a partial response, it will have many more elements in reality but
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
# these are the ones we care about at the moment
def
kube_pod
(
name:
"kube-pod"
,
app:
"valid-pod-label"
,
status:
"Running"
,
track:
nil
)
def
kube_pod
(
name:
"kube-pod"
,
app:
"valid-pod-label"
,
status:
"Running"
,
track:
nil
)
...
@@ -224,6 +242,54 @@ module KubernetesHelpers
...
@@ -224,6 +242,54 @@ module KubernetesHelpers
}
}
end
end
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
}
"
},
"spec"
=>
{
"generation"
=>
2
},
"status"
=>
{
"domain"
=>
"
#{
name
}
.
#{
namespace
}
.
#{
domain
}
"
,
"domainInternal"
=>
"
#{
name
}
.
#{
namespace
}
.svc.cluster.local"
,
"latestCreatedRevisionName"
=>
"
#{
name
}
-00002"
,
"latestReadyRevisionName"
=>
"
#{
name
}
-00002"
,
"observedGeneration"
=>
2
}
}
end
def
kube_service_full
(
name:
"kubetest"
,
namespace:
"kube-ns"
,
domain:
"example.com"
)
{
"metadata"
=>
{
"creationTimestamp"
=>
"2018-11-21T06:16:33Z"
,
"name"
=>
name
,
"namespace"
=>
namespace
,
"selfLink"
=>
"/apis/serving.knative.dev/v1alpha1/namespaces/
#{
namespace
}
/services/
#{
name
}
"
,
"annotation"
=>
{
"description"
=>
"This is a test description"
}
},
"spec"
=>
{
"generation"
=>
2
,
"build"
=>
{
"template"
=>
"go-1.10.3"
}
},
"status"
=>
{
"domain"
=>
"
#{
name
}
.
#{
namespace
}
.
#{
domain
}
"
,
"domainInternal"
=>
"
#{
name
}
.
#{
namespace
}
.svc.cluster.local"
,
"latestCreatedRevisionName"
=>
"
#{
name
}
-00002"
,
"latestReadyRevisionName"
=>
"
#{
name
}
-00002"
,
"observedGeneration"
=>
2
}
}
end
def
kube_terminals
(
service
,
pod
)
def
kube_terminals
(
service
,
pod
)
pod_name
=
pod
[
'metadata'
][
'name'
]
pod_name
=
pod
[
'metadata'
][
'name'
]
containers
=
pod
[
'spec'
][
'containers'
]
containers
=
pod
[
'spec'
][
'containers'
]
...
...
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