Skip to content

Commit

Permalink
Initial PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
mortenlj committed Dec 2, 2021
0 parents commit a877720
Show file tree
Hide file tree
Showing 16 changed files with 721 additions and 0 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Build and deploy elector

on: [push]

env:
image_base: ghcr.io/${{ github.repository }}/elector

jobs:
build:
name: Build and push
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Generate image environment variable
id: set-image-tag
run: |
version=$(./version.sh)
echo "version=${version}" >> $GITHUB_ENV
echo "::set-output name=version::${version}"
echo "ELECTOR_IMAGE=${image_base}:${version}" >> $GITHUB_ENV
echo "::set-output name=image::${ELECTOR_IMAGE}"
- name: Login to GitHub Packages Docker Registry
uses: docker/login-action@ab80d026d4753220c4243394c07c7d80f9638d06 # Use commit-sha1 instead of tag for security concerns
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ env.image_base }}:latest,${{ env.ELECTOR_IMAGE }}
outputs:
image: "${{ steps.set-image-tag.outputs.image }}"
version: "${{ steps.set-image-tag.outputs.version }}"

# TODO: How do we deploy new version of sidecar? Look to wonderwall
44 changes: 44 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
*~
._*
.apdisk
.AppleDB
.AppleDesktop
.AppleDouble
atlassian-ide-plugin.xml
bin
cmake-build-*/
.com.apple.timemachine.donotpresent
com_crashlytics_export_strings.xml
crashlytics-build.properties
crashlytics.properties
.directory
*.dll
.DocumentRevisions-V100
.DS_Store
*.dylib
*.exe
*.exe~
fabric.properties
.fseventsd
.fuse_hidden*
gen
Icon
.idea
.idea_modules/
*.iml
*.iws
.LSOverride
Network Trash Folder
.nfs*
*.out
out
out/
*.so
.Spotlight-V100
.TemporaryItems
Temporary Items
*.test
.Trash-*
.Trashes
.VolumeIcon.icns
.testbin
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @nais/pig-deploy
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Build the manager binary
FROM golang:1.17 as builder

ENV os "linux"
ENV arch "amd64"

# download kubebuilder and extract it to tmp
RUN echo ${os}
RUN echo ${arch}
RUN curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.1/kubebuilder_2.3.1_${os}_${arch}.tar.gz | tar -xz -C /tmp/

# move to a long-term location and put it on your path
# (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else)
RUN mv /tmp/kubebuilder_2.3.1_${os}_${arch} /usr/local/kubebuilder
RUN export PATH=$PATH:/usr/local/kubebuilder/bin

COPY . /workspace
WORKDIR /workspace
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
# Run tests
RUN make test
# Build
RUN CGO_ENABLED=0 GOOS=${os} GOARCH=${arch} GO111MODULE=on go build -a -installsuffix cgo -o elector cmd/elector/main.go

FROM alpine:3
WORKDIR /
COPY --from=builder /workspace/elector /elector

CMD ["/elector"]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 nais

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.
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
K8S_VERSION := 1.19.0
arch := amd64
os := $(shell uname -s | tr '[:upper:]' '[:lower:]')
testbin_dir := ./.testbin/
tools_archive := kubebuilder-tools-${K8S_VERSION}-$(os)-$(arch).tar.gz

elector:
go build -o bin/elector cmd/elector/*.go

test:
go test ./... -count=1 -coverprofile cover.out -short

mocks:
cd pkg && mockery --all --case snake
cd controllers && mockery --all --case snake

kubebuilder: $(testbin_dir)/$(tools_archive)
tar -xzf $(testbin_dir)/$(tools_archive) --strip-components=2 -C $(testbin_dir)
chmod -R +x $(testbin_dir)

$(testbin_dir)/$(tools_archive):
mkdir -p $(testbin_dir)
curl -L -O --output-dir $(testbin_dir) "https://storage.googleapis.com/kubebuilder-tools/$(tools_archive)"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Elector
=======

Simple leader election - PoC
187 changes: 187 additions & 0 deletions cmd/elector/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package main

import (
"context"
"fmt"
"github.com/nais/elector/controllers/leader_election"
"github.com/nais/elector/pkg/election"
"os"
"os/signal"
"strings"
"syscall"
"time"

electormetrics "github.com/nais/elector/pkg/metrics"
log "github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "net/http/pprof" // Enable http profiling
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/metrics"
)

var scheme = runtime.NewScheme()

const (
ExitOK = iota
ExitController
ExitConfig
ExitRuntime
ExitCredentialsManager
)

// Configuration options
const (
KubernetesWriteRetryInterval = "kubernetes-write-retry-interval"
LogFormat = "log-format"
LogLevel = "log-level"
MetricsAddress = "metrics-address"
SyncPeriod = "sync-period"
)

const (
LogFormatJSON = "json"
LogFormatText = "text"
)

func init() {

// Automatically read configuration options from environment variables.
// i.e. --metrics-address will be configurable using ELECTOR_AIVEN_TOKEN.
viper.SetEnvPrefix("elector")
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))

flag.String(MetricsAddress, "127.0.0.1:8080", "The address the metric endpoint binds to.")
flag.String(LogFormat, "text", "Log format, either \"text\" or \"json\"")
flag.String(LogLevel, "info", logLevelHelp())
flag.Duration(KubernetesWriteRetryInterval, time.Second*10, "Requeueing interval when Kubernetes writes fail")
flag.Duration(SyncPeriod, time.Hour*1, "How often to re-synchronize all AivenApplication resources including credential rotation")

flag.Parse()

err := viper.BindPFlags(flag.CommandLine)
if err != nil {
panic(err)
}
}

func logLevelHelp() string {
help := strings.Builder{}
help.WriteString("Log level, one of: ")
notFirst := false
for _, level := range log.AllLevels {
if notFirst {
help.WriteString(", ")
}
help.WriteString(fmt.Sprintf("\"%s\"", level.String()))
notFirst = true
}
return help.String()
}

func formatter(logFormat string) (log.Formatter, error) {
switch logFormat {
case LogFormatJSON:
return &log.JSONFormatter{
TimestampFormat: time.RFC3339Nano,
DisableHTMLEscape: true,
}, nil
case LogFormatText:
return &log.TextFormatter{
FullTimestamp: true,
TimestampFormat: time.RFC3339Nano,
}, nil
}
return nil, fmt.Errorf("unsupported log format '%s'", logFormat)
}

func main() {
logger := log.New()
logfmt, err := formatter(viper.GetString(LogFormat))
if err != nil {
logger.Error(err)
os.Exit(ExitConfig)
}

logger.SetFormatter(logfmt)
level, err := log.ParseLevel(viper.GetString(LogLevel))
if err != nil {
logger.Error(err)
os.Exit(ExitConfig)
}
logger.SetLevel(level)

syncPeriod := viper.GetDuration(SyncPeriod)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
SyncPeriod: &syncPeriod,
Scheme: scheme,
MetricsBindAddress: viper.GetString(MetricsAddress),
})

if err != nil {
logger.Errorln(err)
os.Exit(ExitController)
}

logger.Info("elector running")
terminator := context.Background()
electionResults := make(chan string)

if err := manageLeases(logger, mgr, electionResults); err != nil {
logger.Errorln(err)
os.Exit(ExitCredentialsManager)
}

electionManager := election.NewManager(logger, electionResults)
err = mgr.Add(manager.RunnableFunc(electionManager.Run))
if err != nil {
logger.Errorln(err)
os.Exit(ExitController)
}

go func() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)

for {
select {
case sig := <-signals:
logger.Infof("exiting due to signal: %s", strings.ToUpper(sig.String()))
os.Exit(ExitOK)
}
}
}()

if err := mgr.Start(terminator); err != nil {
logger.Errorln(fmt.Errorf("manager stopped unexpectedly: %s", err))
os.Exit(ExitRuntime)
}

logger.Errorln(fmt.Errorf("manager has stopped"))
}

func manageLeases(logger *log.Logger, mgr manager.Manager, electionResults chan<- string) error {
reconciler := leader_election.NewReconciler(mgr, logger, electionResults)

if err := reconciler.SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to set up reconciler: %s", err)
}
logger.Info("Lease reconciler setup complete")

return nil
}

func init() {
err := clientgoscheme.AddToScheme(scheme)
if err != nil {
panic(err)
}

electormetrics.Register(metrics.Registry)
// +kubebuilder:scaffold:scheme
}
Loading

0 comments on commit a877720

Please sign in to comment.