Skip to content
This repository has been archived by the owner on May 3, 2021. It is now read-only.

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanSussman committed Oct 26, 2017
0 parents commit 8ce1217
Show file tree
Hide file tree
Showing 602 changed files with 196,648 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
release/*
user.json
admin.json
.DS_Store
16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Contributing to reuse

## Issues

Feel free to submit bugs or feature requests as issues.

## Pull Requests

These rules must be followed for any contributions to be merged into master.

1. Fork this repo
1. Make any desired changes
1. Validate you changes meet your desired use case
1. Ensure the `JENKINS_VER` and `JENKINS_REL` in `update.sh` are modified accordingly
1. Ensure documentation has been updated
1. Open a pull-request
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The MIT License (MIT)

Copyright (C) 2017 Target Brands, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 changes: 18 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

{
"version": 1,
"file_format": "This MAINTAINERS file format is described at http://pup.pt/maintainers",
"issues": "https://github.com/target/gelvedere/issues",
"people": [
{
"github": "JordanSussman",
"email": "[email protected]",
"name": "Jordan Sussman"
},
{
"github": "jbrockopp",
"email": "[email protected]",
"name": "Jordan Brockopp"
}
]
}
118 changes: 118 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# gelvedere

[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE)
[![release](http://img.shields.io/github/release/target/gelvedere.svg)](https://github.com/target/gelvedere/releases/latest)
[![watch](https://img.shields.io/github/watchers/target/gelvedere.svg?style=social)](https://github.com/target/gelvedere/watchers)
[![star](https://img.shields.io/github/stars/target/gelvedere.svg?style=social)](https://github.com/target/gelvedere/stargazers)

Cli to deploy a Jenkins master within the JAYS architecture

## How It Works

gelvedere provides a CLI for creating a Jenkins master within Docker swarm. Currently there are 2 types of input files required.

* `admin.json` - contains information specific to deploying the master within docker swarm
* `user.json` - contains information specific to Jenkins ACL configuration

## Getting Started

A sample command to run gelvedere:

```console
$ gelvedere --user-config /jenkins/user-configs/test.json --admin-config /jenkins/admin-configs/test.json --domain acme.com
```

### Input files

A sample `user.json` file:

```json
{
"name": "example",
"admins": "target*Jenkins",
"members": "",
"team": "Jenkins"
}
```

A sample `admin.json` file:

```json
{
"ghe_key": "1234",
"ghe_secret": "56789",
"port": "50000",
"admin_ssh_pubkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVr+0LAocyLbzzvQEdwjU8o+w0IYpR4R0uf2mswNYz6utcUVqHp5VXFog6YL4gYf0Q7naorLGh/zbROGHmBGAUngUbvy1vAnyiiBEjLPhW5k6iLy9f3N2lZyDQJ/VZYeRzfSeOPyEfd13MOjR8kB0zrodFa5j3fIToUrPmLytAVWplbF002jjJOTjwhFaknbdcVTzQ1LxhaOCaVjbEQyuFB3e8mB15kGEJOllnq4Un1HnG6wOcSx8IwP/E1JcmChfM3pPY2PIpYRqYaT4SYKGua+qke90aPNFl/k3j3J3yl2ZKGno/tJjj50sbTDgNz46uTLuLI2Eb6ETeI3d2Jy0Z [email protected]",
"size": "small",
"image": "target/jenkins-docker-master:2.73.1-1"
}
```

Information on generating the values for the `user.json` and `admin.json` file can be found [here](https://github.com/target/jenkins-docker-master/tree/master/examples)

### Storage

gelvedere makes the assumption that you are using local storage for each master, but you can override the storage path with a environment variable or cli argument.

The below example will create a docker swarm service with a mount source of `/jenkins/stores` and a target of `/var/jenkins_home`.

```console
$ gelvedere --user-config /jenkins/user-configs/stores.json --admin-config /jenkins/admin-configs/stores.json --mount-path /jenkins/stores
```

### Custom Environment Variables

By default gelvedere adds the following environment variables to the Docker swarm service:

* JENKINS_ACL_MEMBERS_admin
* JENKINS_ACL_MEMBERS_developer
* JENKINS_URL
* GHE_KEY
* GHE_SECRET
* ADMIN_SSH_PUBKEY
* JENKINS_SLAVE_AGENT_PORT
* JAVA_OPTS

More information on the above configuration options can be found [here](https://github.com/target/jenkins-docker-master/tree/master/examples#usage)

You can add additional variables by setting the following configuration in the `admin.json` input file.

```json
{
"env_variables": {
"<variable name>": "<variable value>",
"<variable name>": "<variable value>"
}
}
```

### Custom docker logging

By default gelvedere does not create Docker swarm services with [logging drivers](https://docs.docker.com/engine/admin/logging/overview/), but you can override that behavior by adding contents into the `admin.json` file.

The below example sends logs in the gelf format

```json
"log_config": {
"driver": "gelf",
"gelf-address": "udp://gelf.example.com:12201"
}
```

## CLI Installation

### Install on Linux

```console
curl -L https://github.com/target/gelvedere/releases/download/v0.1.0/gelvedere-linux-amd64.tar.gz | tar zx

sudo install -t /usr/local/bin gelvedere
```

### Install on macOS

```console
curl -L https://github.com/target/gelvedere/releases/download/v0.1.0/gelvedere-darwin-amd64.tar.gz | tar zx

sudo cp gelvedere /usr/local/bin/
```
41 changes: 41 additions & 0 deletions client/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package client

import (
"context"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
)

// GetDockerSwarmServices gets docker swarm services
func GetDockerSwarmServices() ([]swarm.Service, error) {
ctx := context.Background()
cli, err := client.NewEnvClient()
if err != nil {
return nil, err
}

services, err := cli.ServiceList(ctx, types.ServiceListOptions{})
if err != nil {
return nil, err
}

return services, nil
}

// CreateDockerSwarmService creates docker swarm service
func CreateDockerSwarmService(serviceSpec swarm.ServiceSpec) (*types.ServiceCreateResponse, error) {
ctx := context.Background()
cli, err := client.NewEnvClient()
if err != nil {
return nil, err
}

resp, err := cli.ServiceCreate(ctx, serviceSpec, types.ServiceCreateOptions{})
if err != nil {
return nil, err
}

return &resp, nil
}
70 changes: 70 additions & 0 deletions client/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package client

import (
"encoding/json"
"fmt"
"io/ioutil"

"github.com/target/gelvedere/model"

"github.com/docker/docker/api/types/swarm"
)

// CheckName checks if the docker swarm service name is already in use
func CheckName(serviceName string, services []swarm.Service) error {
for _, service := range services {
if service.Spec.Name == serviceName {
return fmt.Errorf("service %v is already in use", serviceName)
}
}

return nil
}

// CheckPort checks if the port is already in use
func CheckPort(servicePort int, services []swarm.Service) error {
for _, service := range services {
if service.Endpoint.Ports != nil {
for _, v := range service.Endpoint.Ports {
if v.PublishedPort == uint32(servicePort) {
return fmt.Errorf("port %v is already in use", servicePort)
}
}
}
}
return nil
}

// GetAdminJSON returns a json config for admins
func GetAdminJSON(file string) (*model.AdminConfig, error) {
bytes, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}

// Unmarshal JSON contents to AdminConfig
var ac model.AdminConfig
err = json.Unmarshal(bytes, &ac)
if err != nil {
return nil, err
}

return &ac, nil
}

// GetUserJSON returns a json config for users
func GetUserJSON(file string) (*model.UserConfig, error) {
bytes, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}

// Unmarshal JSON contents to UserConfig
var uc model.UserConfig
err = json.Unmarshal(bytes, &uc)
if err != nil {
return nil, err
}

return &uc, nil
}
56 changes: 56 additions & 0 deletions client/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package client

import (
"encoding/json"
"io/ioutil"
"os"
"testing"

"github.com/docker/docker/api/types/swarm"

"github.com/franela/goblin"
)

func Test_utils(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Tests for client/util.go", func() {
responseFile, err := os.Open("../fixtures/tests/service.json")
if err != nil {
t.Fatalf("Unable to read test data: %s", err.Error())
}
defer func() { _ = responseFile.Close() }()
responseBody, _ := ioutil.ReadAll(responseFile)
var service []swarm.Service
err = json.Unmarshal(responseBody, &service)
g.Describe("The CheckName function", func() {
if err != nil {
t.Fatal("Unable to unmarshal data", err.Error())
}
g.It("should return true if name isn't in use", func() {

err = CheckName("foo", service)
g.Assert(err == nil).IsTrue()
})
g.It("should return false if name is in use", func() {

err = CheckName("angry_goldstine", service)
g.Assert(err == nil).IsFalse()
})
})
g.Describe("The CheckPort function", func() {
if err != nil {
t.Fatal("Unable to unmarshal data", err.Error())
}
g.It("should return true if port isn't in use", func() {

err = CheckPort(50763, service)
g.Assert(err == nil).IsTrue()
})
g.It("should return false if port is in use", func() {

err = CheckPort(50789, service)
g.Assert(err == nil).IsFalse()
})
})
})
}
Loading

0 comments on commit 8ce1217

Please sign in to comment.