Skip to content

Commit 7f366f8

Browse files
author
Zachary Seguin
committed
feat(initial): Initial commit
1 parent 641a4d6 commit 7f366f8

23 files changed

+1897
-0
lines changed

.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
deploy/

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
jupyter-apis

CONTRIBUTING.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Contributing
2+
3+
([Français](#comment-contribuer))
4+
5+
## How to Contribute
6+
7+
When contributing, post comments and discuss changes you wish to make via Issues.
8+
9+
Feel free to propose changes by creating Pull Requests. If you don't have write access, editing a file will create a Fork of this project for you to save your proposed changes to. Submitting a change to a file will write it to a new Branch in your Fork, so you can send a Pull Request.
10+
11+
If this is your first time contributing on GitHub, don't worry! Let us know if you have any questions.
12+
13+
### Security
14+
15+
**Do not post any security issues on the public repository!** See [SECURITY.md](SECURITY.md)
16+
17+
______________________
18+
19+
## Comment contribuer
20+
21+
Lorsque vous contribuez, veuillez également publier des commentaires et discuter des modifications que vous souhaitez apporter par l'entremise des enjeux (Issues).
22+
23+
N'hésitez pas à proposer des modifications en créant des demandes de tirage (Pull Requests). Si vous n'avez pas accès au mode de rédaction, la modification d'un fichier créera une copie (Fork) de ce projet afin que vous puissiez enregistrer les modifications que vous proposez. Le fait de proposer une modification à un fichier l'écrira dans une nouvelle branche dans votre copie (Fork), de sorte que vous puissiez envoyer une demande de tirage (Pull Request).
24+
25+
Si c'est la première fois que vous contribuez à GitHub, ne vous en faites pas! Faites-nous part de vos questions.
26+
27+
### Sécurité
28+
29+
**Ne publiez aucun problème de sécurité sur le dépôt publique!** Voir [SECURITY.md](SECURITY.md)

Dockerfile

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Build with the golang image
2+
FROM golang:1.14-alpine AS build
3+
4+
# Add git
5+
RUN apk add git
6+
7+
# Set workdir
8+
WORKDIR /go/src/github.com/StatCan/jupyter-apis
9+
10+
# Add dependencies
11+
COPY go.mod .
12+
COPY go.sum .
13+
RUN go mod download
14+
15+
# Build
16+
COPY . .
17+
RUN CGO_ENABLED=0 go install .
18+
19+
# Generate final image
20+
FROM scratch
21+
COPY --from=build /go/bin/jupyter-apis /jupyter-apis
22+
ENTRYPOINT [ "/jupyter-apis" ]

LICENSE

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
MIT License
2+
3+
Copyright (c) Her Majesty the Queen in Right of Canada, as represented by the Minister of Statistics Canada, 2020
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
SOFTWARE.

README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[(Français)](#interface-de-programmation-dapplications-jupyter)
2+
3+
## Jupyter Application Programming Interfaces
4+
5+
A Golang replacement for the [Kubeflow](https://github.com/kubeflow/kubeflow) Jupyter Web APIs.
6+
7+
### How to Contribute
8+
9+
See [CONTRIBUTING.md](CONTRIBUTING.md)
10+
11+
### License
12+
13+
Unless otherwise noted, the source code of this project is covered under Crown Copyright, Government of Canada, and is distributed under the [MIT License](LICENSE).
14+
15+
The Canada wordmark and related graphics associated with this distribution are protected under trademark law and copyright law. No permission is granted to use them outside the parameters of the Government of Canada's corporate identity program. For more information, see [Federal identity requirements](https://www.canada.ca/en/treasury-board-secretariat/topics/government-communications/federal-identity-requirements.html).
16+
17+
______________________
18+
19+
## Interface de programmation d'applications Jupyter
20+
21+
Un remplacement Golang pour les API de Web de Jupyter, partie de [Kubeflow](https://github.com/kubeflow/kubeflow).
22+
23+
### Comment contribuer
24+
25+
Voir [CONTRIBUTING.md](CONTRIBUTING.md)
26+
27+
### Licence
28+
29+
Sauf indication contraire, le code source de ce projet est protégé par le droit d'auteur de la Couronne du gouvernement du Canada et distribué sous la [licence MIT](LICENSE).
30+
31+
Le mot-symbole « Canada » et les éléments graphiques connexes liés à cette distribution sont protégés en vertu des lois portant sur les marques de commerce et le droit d'auteur. Aucune autorisation n'est accordée pour leur utilisation à l'extérieur des paramètres du programme de coordination de l'image de marque du gouvernement du Canada. Pour obtenir davantage de renseignements à ce sujet, veuillez consulter les [Exigences pour l'image de marque](https://www.canada.ca/fr/secretariat-conseil-tresor/sujets/communications-gouvernementales/exigences-image-marque.html).

SECURITY.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
([Français](#sécurité))
2+
3+
# Security
4+
5+
**Do not post any security issues on the public repository!** Security vulnerabilities must be reported by email to `[email protected]`
6+
7+
______________________
8+
9+
## Sécurité
10+
11+
**Ne publiez aucun problème de sécurité sur le dépôt publique!** Les vulnérabilités de sécurité doivent être signalées par courriel à `[email protected]`

access.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"net/url"
8+
9+
"github.com/gorilla/mux"
10+
authorizationv1 "k8s.io/api/authorization/v1"
11+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
)
13+
14+
// kubeflowUserHandler returns an http.Handler which
15+
// wraps the provided handler h, and will
16+
// load the User ID of the user from the specific
17+
// header in the request and add it to the HTTP request information.
18+
func kubeflowUserHandler(header string, h http.Handler) http.Handler {
19+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20+
user := r.Header.Get(header)
21+
if user != "" {
22+
// Add the User information to the request.
23+
r.URL.User = url.User(user)
24+
}
25+
26+
h.ServeHTTP(w, r)
27+
})
28+
}
29+
30+
// checkAccess is a middleware HTTP handler function that will verify the
31+
// provided user's access against the Kubernetes API server. It loads the
32+
// user's ID from the `kubeflow-userid` Header, generated a SubjectAccessReview
33+
// request and submits it to the Kubernetes API server. The API server will
34+
// return whether the user is permitted to perform the requested action.
35+
//
36+
// subjectAccessReviewTemplate: A authorization.k8s.io/v1 SubjectAccessReview
37+
// object used as a template for the request.
38+
// Note: the spec.User and Spec.ResourceAttributes.Namespace are REPLACED by this function.
39+
// next: The next handler to call if the user is authorized to access the desired resource
40+
func (s *server) checkAccess(subjectAccessReviewTemplate authorizationv1.SubjectAccessReview, next http.HandlerFunc) http.HandlerFunc {
41+
return func(w http.ResponseWriter, r *http.Request) {
42+
vars := mux.Vars(r)
43+
44+
// Make a copy of the template, so we don't replace content in the templated version
45+
sar := subjectAccessReviewTemplate.DeepCopy()
46+
47+
// Load the user from kubeflow-userid. If there is no user provided,
48+
// then do not continue to process the request.
49+
user := r.URL.User.Username()
50+
if user == "" {
51+
log.Printf("no user found")
52+
s.respond(w, r, APIResponse{
53+
Success: false,
54+
Log: "no user found",
55+
})
56+
return
57+
}
58+
59+
// Update the SubjectAccessReview request with the namespace and user information
60+
sar.Spec.ResourceAttributes.Namespace = vars["namespace"]
61+
sar.Spec.User = user
62+
63+
// Submit the SubjectAccessReview to the Kubernetes API server
64+
resp, err := s.clientsets.kubernetes.AuthorizationV1().SubjectAccessReviews().Create(r.Context(), sar, v1.CreateOptions{})
65+
if err != nil {
66+
s.error(w, r, err)
67+
return
68+
}
69+
70+
// If the user is not permitted, log and return the error and do not process the request
71+
if !resp.Status.Allowed {
72+
msg := fmt.Sprintf("User %s is not permitted to %s %s.%s.%s for namespace: %s", sar.Spec.User, sar.Spec.ResourceAttributes.Verb, sar.Spec.ResourceAttributes.Group, sar.Spec.ResourceAttributes.Version, sar.Spec.ResourceAttributes.Resource, sar.Spec.ResourceAttributes.Namespace)
73+
74+
log.Println(msg)
75+
76+
s.respond(w, r, APIResponse{
77+
Success: false,
78+
Log: msg,
79+
})
80+
return
81+
}
82+
83+
// Finally, if the user is authorized
84+
next(w, r)
85+
}
86+
}

deploy/deploy.yaml

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
labels:
5+
app.kubernetes.io/component: jupyter-apis
6+
app.kubernetes.io/instance: jupyter-apis
7+
app.kubernetes.io/name: jupyter-apis
8+
name: jupyter-apis
9+
namespace: kubeflow
10+
spec:
11+
selector:
12+
matchLabels:
13+
app.kubernetes.io/component: jupyter-apis
14+
app.kubernetes.io/instance: jupyter-apis
15+
app.kubernetes.io/name: jupyter-apis
16+
template:
17+
metadata:
18+
labels:
19+
app.kubernetes.io/component: jupyter-apis
20+
app.kubernetes.io/instance: jupyter-apis
21+
app.kubernetes.io/name: jupyter-apis
22+
spec:
23+
containers:
24+
- image: zachomedia/jupyter-apis:latest
25+
imagePullPolicy: Always
26+
name: jupyter-web-app
27+
ports:
28+
- containerPort: 8080
29+
protocol: TCP
30+
name: http
31+
resources: {}
32+
imagePullSecrets:
33+
- name: k8scc01covidacr-registry-connection
34+
serviceAccountName: jupyter-web-app-service-account
35+
---
36+
apiVersion: v1
37+
kind: Service
38+
metadata:
39+
name: jupyter-apis
40+
spec:
41+
type: ClusterIP
42+
selector:
43+
app.kubernetes.io/component: jupyter-apis
44+
app.kubernetes.io/instance: jupyter-apis
45+
app.kubernetes.io/name: jupyter-apis
46+
ports:
47+
- name: http
48+
port: 80
49+
targetPort: http
50+
---
51+
apiVersion: networking.istio.io/v1alpha3
52+
kind: VirtualService
53+
metadata:
54+
labels:
55+
app.kubernetes.io/component: jupyter-web-app
56+
app.kubernetes.io/instance: jupyter-web-app-v1.0.0
57+
app.kubernetes.io/managed-by: kfctl
58+
app.kubernetes.io/name: jupyter-web-app
59+
app.kubernetes.io/part-of: kubeflow
60+
app.kubernetes.io/version: v1.0.0
61+
name: jupyter-web-app
62+
namespace: kubeflow
63+
spec:
64+
gateways:
65+
- kubeflow-gateway
66+
hosts:
67+
- '*'
68+
http:
69+
- match:
70+
- uri:
71+
prefix: /jupyter/api/storageclasses/
72+
method:
73+
exact: GET
74+
rewrite:
75+
uri: /api/storageclasses/
76+
route:
77+
- destination:
78+
host: jupyter-apis.kubeflow.svc.cluster.local
79+
port:
80+
number: 80
81+
- match:
82+
- uri:
83+
prefix: /jupyter/api/namespaces/
84+
method:
85+
exact: GET
86+
rewrite:
87+
uri: /api/namespaces/
88+
route:
89+
- destination:
90+
host: jupyter-apis.kubeflow.svc.cluster.local
91+
port:
92+
number: 80
93+
- headers:
94+
request:
95+
add:
96+
x-forwarded-prefix: /jupyter
97+
match:
98+
- uri:
99+
prefix: /jupyter/
100+
rewrite:
101+
uri: /
102+
route:
103+
- destination:
104+
host: jupyter-web-app-service.kubeflow.svc.cluster.local
105+
port:
106+
number: 80

deploy/restore.yaml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
apiVersion: networking.istio.io/v1alpha3
2+
kind: VirtualService
3+
metadata:
4+
labels:
5+
app.kubernetes.io/component: jupyter-web-app
6+
app.kubernetes.io/instance: jupyter-web-app-v1.0.0
7+
app.kubernetes.io/managed-by: kfctl
8+
app.kubernetes.io/name: jupyter-web-app
9+
app.kubernetes.io/part-of: kubeflow
10+
app.kubernetes.io/version: v1.0.0
11+
name: jupyter-web-app
12+
namespace: kubeflow
13+
spec:
14+
gateways:
15+
- kubeflow-gateway
16+
hosts:
17+
- '*'
18+
http:
19+
- headers:
20+
request:
21+
add:
22+
x-forwarded-prefix: /jupyter
23+
match:
24+
- uri:
25+
prefix: /jupyter/
26+
rewrite:
27+
uri: /
28+
route:
29+
- destination:
30+
host: jupyter-web-app-service.kubeflow.svc.cluster.local
31+
port:
32+
number: 80

go.mod

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module github.com/StatCan/jupyter-apis
2+
3+
go 1.14
4+
5+
require (
6+
github.com/StatCan/kubeflow-controller v0.0.0-20200805150330-c19fa4b0fcb6
7+
github.com/andanhm/go-prettytime v1.0.0
8+
github.com/gorilla/handlers v1.4.2
9+
github.com/gorilla/mux v1.7.4
10+
github.com/hashicorp/vault/api v1.0.4 // indirect
11+
github.com/imdario/mergo v0.3.10 // indirect
12+
github.com/kr/pretty v0.2.0 // indirect
13+
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect
14+
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
15+
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
16+
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
17+
k8s.io/api v0.18.6
18+
k8s.io/apimachinery v0.18.6
19+
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
20+
sigs.k8s.io/controller-runtime v0.0.0-00010101000000-000000000000
21+
)
22+
23+
replace k8s.io/client-go => k8s.io/client-go v0.18.6
24+
25+
replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.18.6
26+
27+
replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.6.2
28+
29+
replace github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.4.0

0 commit comments

Comments
 (0)