Skip to content

Commit 2dfa32e

Browse files
committed
Merge pull request #36 from AcalephStorage/release/0.1.2
Release 0.1.2
2 parents 6838699 + 26206f4 commit 2dfa32e

22 files changed

+796
-303
lines changed

.pipeline.yml

+1-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ spec:
1616
app: kontinuous
1717
type: ci-cd
1818
secrets:
19-
- docker-credentials
19+
- quayregistrycreds
2020
stages:
2121
- name: Build Docker Image
2222
type: docker_build
@@ -32,6 +32,3 @@ spec:
3232
external_registry: quay.io
3333
external_image_name: acaleph/kontinuous
3434
require_credentials: "TRUE"
35-
username: user
36-
password: password
37-
email: email

Dockerfile

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.6
1+
FROM golang:1.6-alpine
22

33
ENV GOPATH /go
44
ENV SWAGGER_UI /swagger/dist
@@ -9,6 +9,15 @@ WORKDIR /go/src/github.com/AcalephStorage/kontinuous
99
RUN mkdir /swagger && tar xvzf third_party/swagger.tar.gz -C /swagger
1010

1111
# create and remove downloaded libraries
12-
RUN make && rm -rf /go/bin && rm -rf /go/lib
12+
RUN apk update && \
13+
apk add make git && \
14+
make && \
15+
mv build/bin/kontinuous /bin && \
16+
mv build/bin/kontinuous-cli /bin && \
17+
rm -rf /go && \
18+
apk del --purge make git && \
19+
rm -rf /var/cache/apk/*
1320

14-
ENTRYPOINT build/bin/kontinuous
21+
EXPOSE 3005
22+
23+
ENTRYPOINT /bin/kontinuous

README.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ Alternatively, for more customization, a sample yaml file for running kontinuous
3838

3939
Once running, add a [.pipeline.yml](#pipeline-spec) to the root of your Github repo and configure the webhooks.
4040

41+
Example pipelines can be found in [/examples](./examples)
42+
4143
The [CLI client](#clients) or [API](#api) can be used to view build status or logs.
4244

4345
### Dependencies
@@ -78,7 +80,7 @@ The following environment variables needs to be defined:
7880

7981
A Kubernetes Secret also needs to be defined and mounted to the Pod. The secret should have a key named `kontinuous-secrets` and should contain the following data (must be base64 encoded):
8082

81-
```
83+
```json
8284
{
8385
"AuthSecret": "base64 encoded auth secret",
8486
"S3SecretKey": "s3 secret key",
@@ -102,7 +104,7 @@ The repository needs to define a build pipeline in the repository root called `.
102104

103105
Here's a sample `.pipeline.yml`:
104106

105-
```
107+
```yaml
106108
---
107109
apiVersion: v1alpha1
108110
kind: Pipeline
@@ -157,7 +159,7 @@ The format is something similar to K8s Specs. Here are more details on some of t
157159

158160
The general definition of a stage is:
159161

160-
```
162+
```yaml
161163
name: Friendly name
162164
type: {docker_build,command,docker_publish}
163165
params:
@@ -242,9 +244,9 @@ Make sure to enable access to the following:
242244

243245
The script `scripts/jwt-gen` can generate a JSON Web Token to be used for authentication with Kontinuous.
244246

245-
```
247+
```console
246248
$ scripts/jwt-gen --secret {base64url encoded secret} --github-token {github-token}
247-
```
249+
```
248250

249251
This generates a JSON Web Token and can be added to the request header as `Authorization: Bearer {token}` to authenticate requests.
250252

@@ -266,13 +268,13 @@ A Web based Dashboard is under development.
266268

267269
Building `kontinuous` from source is done by:
268270

269-
```
271+
```console
270272
$ make deps build
271273
```
272274

273275
Build the docker image:
274276

275-
```
277+
```console
276278
$ docker build -t {tag} .
277279
```
278280

api/auth.go

+124
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,42 @@ package api
22

33
import (
44
"errors"
5+
"fmt"
56
"os"
67
"strings"
78

89
"encoding/base64"
10+
"encoding/json"
11+
"io/ioutil"
912
"net/http"
13+
"net/url"
1014

1115
"github.com/dgrijalva/jwt-go"
1216
"github.com/emicklei/go-restful"
17+
18+
"github.com/AcalephStorage/kontinuous/pipeline"
19+
"github.com/AcalephStorage/kontinuous/store/kv"
1320
)
1421

22+
type GithubAuthResponse struct {
23+
AccessToken string `json:"access_token"`
24+
}
25+
1526
// JWTClaims contains the claims from the jwt
1627
type JWTClaims struct {
1728
GithubAccessToken string
1829
}
1930

31+
type AuthResource struct {
32+
JWTClaims
33+
kv.KVClient
34+
}
35+
36+
type AuthResponse struct {
37+
JWT string `json:"jwt"`
38+
UserID string `json:"user_id"`
39+
}
40+
2041
var (
2142
claims JWTClaims
2243

@@ -58,6 +79,109 @@ var (
5879
}
5980
)
6081

82+
func (a *AuthResource) Register(container *restful.Container) {
83+
ws := new(restful.WebService)
84+
85+
ws.
86+
Path("/login").
87+
Consumes(restful.MIME_JSON).
88+
Produces(restful.MIME_JSON).
89+
Filter(ncsaCommonLogFormatLogger)
90+
91+
ws.Route(ws.POST("github").To(a.githubLogin).
92+
Writes(AuthResponse{}).
93+
Doc("Generate JWT for API authentication").
94+
Operation("authorize"))
95+
96+
container.Add(ws)
97+
}
98+
99+
func (a *AuthResource) githubLogin(req *restful.Request, res *restful.Response) {
100+
101+
dsecret := os.Getenv("AUTH_SECRET")
102+
103+
authCode := req.QueryParameter("code")
104+
state := req.QueryParameter("state")
105+
106+
if len(authCode) == 0 {
107+
jsonError(res, http.StatusUnauthorized, errors.New("Missing Authorization Code"), "No authorization code provided")
108+
return
109+
}
110+
111+
// request url
112+
reqUrl := url.URL{
113+
Scheme: "https",
114+
Host: "github.com",
115+
Path: "login/oauth/access_token",
116+
}
117+
q := reqUrl.Query()
118+
q.Set("client_id", os.Getenv("GH_CLIENT_ID"))
119+
q.Set("client_secret", os.Getenv("GH_CLIENT_SECRET"))
120+
q.Set("code", authCode)
121+
q.Set("state", state)
122+
reqUrl.RawQuery = q.Encode()
123+
124+
client := &http.Client{}
125+
126+
r, err := http.NewRequest("POST", reqUrl.String(), nil)
127+
if err != nil {
128+
jsonError(res, http.StatusUnauthorized, err, "Error creating auth request")
129+
return
130+
}
131+
r.Header.Add("Accept", "application/json")
132+
133+
authRes, err := client.Do(r)
134+
if err != nil {
135+
jsonError(res, http.StatusUnauthorized, err, "Error requesting authorization token")
136+
return
137+
}
138+
defer authRes.Body.Close()
139+
140+
body, err := ioutil.ReadAll(authRes.Body)
141+
if err != nil {
142+
jsonError(res, http.StatusUnauthorized, err, "Error reading response body")
143+
return
144+
}
145+
146+
var ghRes GithubAuthResponse
147+
if err := json.Unmarshal(body, &ghRes); err != nil {
148+
jsonError(res, http.StatusUnauthorized, err, "Error reading json body")
149+
return
150+
}
151+
152+
accessToken := ghRes.AccessToken
153+
154+
jwtToken, err := CreateJWT(accessToken, string(dsecret))
155+
if err != nil {
156+
jsonError(res, http.StatusUnauthorized, err, "Unable to create jwt for user")
157+
return
158+
}
159+
160+
ghUser, err := GetGithubUser(accessToken)
161+
if err != nil {
162+
jsonError(res, http.StatusUnauthorized, err, "Unable to get github user")
163+
return
164+
}
165+
166+
userID := fmt.Sprintf("github|%v", ghUser.ID)
167+
user := &pipeline.User{
168+
Name: ghUser.Login,
169+
RemoteID: userID,
170+
Token: accessToken,
171+
}
172+
if err := user.Save(a.KVClient); err != nil {
173+
jsonError(res, http.StatusUnauthorized, err, "Unable to register user")
174+
return
175+
}
176+
177+
entity := &AuthResponse{
178+
JWT: jwtToken,
179+
UserID: userID,
180+
}
181+
182+
res.WriteEntity(entity)
183+
}
184+
61185
func parseToken(req *restful.Request) string {
62186
// apply the same checking as jwt.ParseFromRequest
63187
if ah := req.HeaderParameter("Authorization"); ah != "" {

api/build.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package api
33
import (
44
"errors"
55
"fmt"
6-
"strconv"
76
"time"
87

98
"encoding/json"
@@ -100,6 +99,12 @@ func (b *BuildResource) create(req *restful.Request, res *restful.Response) {
10099
return
101100
}
102101

102+
//check if .pipeline exist in branch
103+
if _, err := pipeline.Definition(hook.Commit, client); err != nil {
104+
jsonError(res, http.StatusInternalServerError, err, "Unable to create build. pipeline")
105+
return
106+
}
107+
103108
// persist build
104109
build := &ps.Build{
105110
Author: hook.Author,
@@ -122,8 +127,8 @@ func (b *BuildResource) create(req *restful.Request, res *restful.Response) {
122127
return
123128
}
124129

125-
//save notif details in pipeline
126-
pipeline.SaveNotifiers(definition, b.KVClient)
130+
//update details in pipeline
131+
pipeline.UpdatePipeline(definition, b.KVClient)
127132

128133
// save stage details
129134
build.Stages = definition.GetStages()
@@ -135,7 +140,7 @@ func (b *BuildResource) create(req *restful.Request, res *restful.Response) {
135140

136141
stageStatus := &ps.StatusUpdate{
137142
Status: ps.BuildFailure,
138-
Timestamp: strconv.FormatInt(time.Now().UnixNano(), 10),
143+
Timestamp: time.Now().UnixNano(),
139144
}
140145
stage, err := findStage("1", build, b.KVClient)
141146
if err != nil {
@@ -181,7 +186,7 @@ func (b *BuildResource) list(req *restful.Request, res *restful.Response) {
181186
return
182187
}
183188

184-
builds, err := pipeline.GetBuilds(b.KVClient)
189+
builds, err := pipeline.GetAllBuildsSummary(b.KVClient)
185190
if err != nil {
186191
jsonError(res, http.StatusInternalServerError, err, fmt.Sprintf("Unable to list builds for %s/%s", owner, repo))
187192
return

0 commit comments

Comments
 (0)