Skip to content

Commit

Permalink
Initial fleetlock server implementation
Browse files Browse the repository at this point in the history
* Add initial Dockerfile and image build
* Add LICENSE, DCO, and CONTRIBUTING
  • Loading branch information
dghubble committed Aug 23, 2020
0 parents commit 8681e07
Show file tree
Hide file tree
Showing 16 changed files with 747 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin/
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# fleetlock

Notable changes between versions.
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Contributing

## Developer Certificate of Origin

By contributing, you agree to the Linux Foundation's Developer Certificate of Origin ([DCO](DCO)). The DCO is a statement that you, the contributor, have the legal right to make your contribution and understand the contribution will be distributed as part of this project.
36 changes: 36 additions & 0 deletions DCO
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM docker.io/alpine:3.12
MAINTAINER Dalton Hubble <dghubble@gmail.com>

RUN apk --no-cache --update add ca-certificates
COPY bin/fleetlock /usr/local/bin

USER nobody
ENTRYPOINT ["/usr/local/bin/fleetlock"]

24 changes: 24 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
The MIT License (MIT)

Copyright (c) 2020 Poseidon Labs
Copyright (c) 2020 Dalton Hubble

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.


67 changes: 67 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export CGO_ENABLED:=0
export GO111MODULE=on
export GOFLAGS=-mod=vendor

DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
VERSION=$(shell git describe --tags --match=v* --always --abbrev=0 --dirty)

REPO=github.com/poseidon/fleetlock
LOCAL_REPO=dghubble/fleetlock
IMAGE_REPO=quay.io/dghubble/fleetlock

LD_FLAGS="-w -X $(REPO)/cmd.version=$(VERSION)"

.PHONY: all
all: build test vet lint fmt

.PHONY: build
build: bin/fleetlock

.PHONY: bin/fleetlock
bin/fleetlock:
@go build -o bin/fleetlock -ldflags $(LD_FLAGS) $(REPO)/cmd/fleetlock

.PHONY: test
test:
@go test ./... -cover

.PHONY: vet
vet:
@go vet -all ./...

.PHONY: lint
lint:
@golint -set_exit_status `go list ./...`

.PHONY: fmt
fmt:
@test -z $$(go fmt ./...)

.PHONY: image
image:
buildah bud -t $(LOCAL_REPO):$(VERSION) .
buildah tag $(LOCAL_REPO):$(VERSION) $(LOCAL_REPO):latest

.PHONY: push
push:
buildah tag $(LOCAL_REPO):$(VERSION) $(IMAGE_REPO):$(VERSION)
buildah tag $(LOCAL_REPO):$(VERSION) $(IMAGE_REPO):latest
buildah push docker://$(IMAGE_REPO):$(VERSION)
buildah push docker://$(IMAGE_REPO):latest

.PHONY: update
update:
@GOFLAGS="" go get -u ./...
@go mod tidy

.PHONY: vendor
vendor:
@go mod vendor

lock:
curl -H "fleet-lock-protocol: true" -d @examples/body.json http://127.0.0.1:8080/v1/pre-reboot

unlock:
curl -H "fleet-lock-protocol: true" -d @examples/body.json http://127.0.0.1:8080/v1/steady-state


139 changes: 139 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# fleetlock [![GoDoc](https://godoc.org/github.com/poseidon/fleetlock?status.png)](https://godoc.org/github.com/poseidon/fleetlock) [![Quay](https://img.shields.io/badge/container-quay-green)](https://quay.io/repository/poseidon/fleetlock)

`fleetlock` is a reboot coordinator for Fedora CoreOS nodes in Kubernetes clusters. It implements the [FleetLock](https://github.com/coreos/airlock/pull/1/files) protocol for use as a [Zincati](https://github.com/coreos/zincati) lock [strategy](https://github.com/coreos/zincati/blob/master/docs/usage/updates-strategy.md) backend.

## Usage

Zincati runs on-host (`zincati.service`). Declare a Zincati `fleet_lock` strategy when provisioning Fedora CoreOS nodes. Set `base_url` for host nodes to access the in-cluster `fleetlock` Service (e.g. known ClusterIP).

```yaml
variant: fcos
version: 1.0.0
storage:
files:
- path: /etc/zincati/config.d/55-update-strategy.toml
contents:
inline: |
[updates]
strategy = "fleet_lock"
[updates.fleet_lock]
base_url = "http://10.3.0.15/"
```
Apply the `fleetlock` Deployment, Service (with ClusterIP), and ServiceAccount.

```
kubectl apply -f examples/
```
Inspect the fleetlock Lease object.
```
$ kubectl get leases -n default
NAME HOLDER AGE
fleetlock-default 049ad0f57ade4723a48692b7b692c318 4m50s
```
### Configuration
Configure the server via flags.
| flag | description | default |
|------------|--------------|--------------|
| -address | HTTP listen address | 0.0.0.0:8080 |
| -log-level | Logger level | info |
| -version | Show version | NA |
| -help | Show help | NA |
Or via environment variables.
| variable | description | default |
|------------|------------------------|-----------|
| NAMESPACE | Kubernetes Namespace | "default" |
| KUBECONFIG | Development Kubeconfig | NA |
### Typhoon
For Typhoon clusters, add the Zincati config a [snippet](https://typhoon.psdn.io/advanced/customization/#fedora-coreos).
```tf
module "nemo" {
...
controller_snippets = [
file("./snippets/zincati-strategy.yaml"),
]
worker_snippets = [
file("./snippets/zincati-strategy.yaml"),
]
}
```

## Manual Intervention

`fleetlock` coordinates OS auto-updates to avoid concurrent node updates or a potential bad auto-update continuing. Zincati obtains a reboot lease lock before finalization (i.e reboot).

If an auto-update fails, the lease continues to be held by design. An admin should investigate the node failure and decide whether it is safe to remove the lease.

```
$ kubectl get leases
$ kubectl delete lease fleetlock-default
```

## Development

To develop locally, build and run the executable.

### Static Binary

Build the static binary.

```
make build
```

### Container Image

Build the container image.

```
make image
```

### Run

Run the executable.

```
export KUBECONFIG=some-dev-kubeconfig
./bin/fleetlock
```

Use curl to emulate a Zincati FleetLock client.

```json
{
"client_params": {
"id": "c988d2509fdf5cdcbed39037c56406fb",
"group": "default"
}
}
```

Request a reboot lock.


```
curl -H "fleet-lock-protocol: true" -d @examples/body.json http://127.0.0.1:8080/v1/pre-reboot
```

Release a reboot lock.

```
curl -H "fleet-lock-protocol: true" -d @examples/body.json http://127.0.0.1:8080/v1/steady-state
```

## Related

* [Zincati Guide](https://docs.fedoraproject.org/en-US/fedora-coreos/auto-updates/)
* [Zincati Docs](https://github.com/coreos/zincati/blob/master/docs/usage/updates-strategy.md)
* [FleetLock Protocol](https://github.com/coreos/airlock/pull/1/files)
69 changes: 69 additions & 0 deletions cmd/fleetlock/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"flag"
"fmt"
"net/http"

"github.com/sirupsen/logrus"

"github.com/poseidon/fleetlock"
)

var (
// version provided by compile time -ldflags
version = "was not built properly"
// logger defaults to info logging
log = logrus.New()
)

func main() {
flags := struct {
address string
logLevel string
version bool
help bool
}{}

flag.StringVar(&flags.address, "address", "0.0.0.0:8080", "HTTP listen address")
// log levels https://github.com/sirupsen/logrus/blob/master/logrus.go#L36
flag.StringVar(&flags.logLevel, "log-level", "info", "Set the logging level")
// subcommands
flag.BoolVar(&flags.version, "version", false, "Print version and exit")
flag.BoolVar(&flags.help, "help", false, "Print usage and exit")

// parse command line arguments
flag.Parse()

if flags.version {
fmt.Println(version)
return
}

if flags.help {
flag.Usage()
return
}

// logger
lvl, err := logrus.ParseLevel(flags.logLevel)
if err != nil {
log.Fatalf("invalid log-level: %v", err)
}
log.Level = lvl

// HTTP Server
config := &fleetlock.Config{
Logger: log,
}
server, err := fleetlock.NewServer(config)
if err != nil {
log.Fatalf("main: NewServer error %v", err)
}

log.Infof("main: starting fleetlock on %s", flags.address)
err = http.ListenAndServe(flags.address, server)
if err != nil {
log.Fatalf("main: ListenAndServe error: %v", err)
}
}
34 changes: 34 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package fleetlock

import (
"encoding/json"
"fmt"
"net/http"
)

// Message represents a FleetLock protocol client request.
type Message struct {
ClientParmas struct {
ID string `json:"id"`
Group string `json:"group"`
} `json:"client_params"`
}

// decodeMessage decodes a Message from a request.
func decodeMessage(w http.ResponseWriter, req *http.Request) (*Message, error) {
msg := &Message{}
err := json.NewDecoder(req.Body).Decode(msg)
if err != nil {
return nil, err
}

if msg.ClientParmas.ID == "" {
return nil, fmt.Errorf("message missing id: %v", msg)
}

if msg.ClientParmas.Group == "" {
return nil, fmt.Errorf("message missing group: %v", msg)
}

return msg, nil
}
Loading

0 comments on commit 8681e07

Please sign in to comment.