Skip to content

Commit

Permalink
Go releaser (#2)
Browse files Browse the repository at this point in the history
CI/CD for building cloudcored and cloudcore server
  • Loading branch information
clarkmcc authored Nov 24, 2023
1 parent 2feb2e9 commit 4421919
Show file tree
Hide file tree
Showing 16 changed files with 450 additions and 33 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/build-cloudcored.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Release

on:
push:
# run only against tags
tags:
- "*"

permissions:
contents: write
packages: write
issues: write

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v4
with:
go-version: stable
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# More assembly might be required: Docker logins, GPG, etc.
# It all depends on your needs.
- name: Install Protoc
uses: arduino/setup-protoc@v2
- name: Code Generation
run: |
go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]
go generate ./...
- uses: goreleaser/goreleaser-action@v5
with:
# either 'goreleaser' (default) or 'goreleaser-pro':
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58 changes: 58 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
builds:
- id: cloudcored
main: ./cmd/cloudcored
binary: cloudcored
ldflags:
- -s -w -X github.com/clarkmcc/cloudcore/pkg/version.Version={{.Version}} -X github.com/clarkmcc/cloudcore/pkg/version.Hash={{.Commit}}
env:
- CGO_ENABLED=0
targets:
- darwin_amd64
- darwin_arm64
- linux_amd64
- linux_arm64
- linux_arm_5
- id: cloudcore-server
main: ./cmd/cloudcore-server
binary: cloudcore
ldflags:
- -s -w -X github.com/clarkmcc/cloudcore/pkg/version.Version={{.Version}} -X github.com/clarkmcc/cloudcore/pkg/version.Hash={{.Commit}}
env:
- CGO_ENABLED=0
targets:
- darwin_amd64
- darwin_arm64
- linux_amd64
- linux_arm64
- linux_arm_5
nfpms:
- id: cloudcored-linux
package_name: cloudcored
builds:
- cloudcored
vendor: cloudcore
homepage: https://cloudcore.clarkmccauley.com/
maintainer: Clark McCauley
license: MIT
formats:
- deb
scripts:
preinstall: scripts/linux/preinstall.sh
# todo: Setup systemd service
# postinstall: scripts/linux/postinstall.sh
# preremove: scripts/linux/preremove.sh
# postremove: scripts/linux/postremove.sh
# Whether to enable the size reporting or not.

dockers:
# Server
- id: cloudcore-server
image_templates:
- ghcr.io/clarkmcc/cloudcore
ids:
- cloudcore-server
goos: linux
goarch: amd64
dockerfile: ./cmd/cloudcore-server/Dockerfile

report_sizes: true
5 changes: 5 additions & 0 deletions cmd/cloudcore-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM scratch

COPY cloudcore /cloudcore

ENTRYPOINT ["cloudcore"]
20 changes: 8 additions & 12 deletions cmd/cloudcored/main.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
package main

import (
"context"
"github.com/clarkmcc/cloudcore/internal/agent"
"github.com/clarkmcc/cloudcore/internal/logger"
"github.com/clarkmcc/cloudcore/internal/sysinfo"
"github.com/clarkmcc/cloudcore/internal/tasks"
_ "github.com/clarkmcc/cloudcore/internal/tasks/registered"
"github.com/spf13/cobra"
"go.uber.org/fx"
"go.uber.org/fx/fxevent"
"go.uber.org/zap"
"gopkg.in/tomb.v2"
"os"
"os/signal"
)

var cmd = &cobra.Command{
Use: "cloudcored",
RunE: func(cmd *cobra.Command, args []string) error {
t := tomb.Tomb{}
app := fx.New(
fx.Provide(literal(cmd)),
fx.Provide(signaller(&t)),
fx.Invoke(shutdowner),
fx.Provide(agent.NewConfig),
fx.Provide(agent.NewDatabase),
fx.Provide(agent.NewServer),
Expand All @@ -29,30 +30,25 @@ var cmd = &cobra.Command{
sysinfo.NewSystemMetadataProvider,
fx.As(new(agent.SystemMetadataProvider)))),
fx.Invoke(agent.NewLifecycleNotifications),
fx.Provide(func() (*tomb.Tomb, context.Context) {
tomb := tomb.Tomb{}
ctx, _ := signal.NotifyContext(tomb.Context(context.Background()), os.Interrupt)
return &tomb, ctx
}),
fx.Decorate(func(config *agent.Config) *agent.Logging {
return &config.Logging
}),
fx.Provide(func(config *agent.Config) *zap.Logger {
return logger.New(config.Logging.Level, config.Logging.Debug)
}),
fx.WithLogger(func(logger *zap.Logger) fxevent.Logger {
return &fxevent.ZapLogger{Logger: logger.Named("fx")}
}),
fx.Invoke(func(e *tasks.Executor) {
e.Initialize()
}),
fx.Invoke(func(s fx.Shutdowner, tomb *tomb.Tomb) error {
<-tomb.Dead()
return s.Shutdown(fx.ExitCode(0))
}),
)
err := app.Err()
if err != nil {
return err
}
app.Run()
<-t.Dead()
return nil
},
}
Expand Down
42 changes: 42 additions & 0 deletions cmd/cloudcored/providers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"context"
"go.uber.org/fx"
"go.uber.org/zap"
"gopkg.in/tomb.v2"
"os"
"os/signal"
)

// signaller accepts a global tomb that doesn't need to be provided via
// the fx framework and returns a fx.Provider function that takes control
// of the tomb and connects it to a signal handler. When the signal is
// received, then the tomb is killed.
//
// Why a global tomb rather than a tomb scoped to the fx app you may ask?
// Because we need the final shutdown step of the application to be waiting
// for the tomb to die, and this needs to happen outside the fx app.
func signaller(t *tomb.Tomb) func(logger *zap.Logger) (*tomb.Tomb, context.Context) {
return func(logger *zap.Logger) (*tomb.Tomb, context.Context) {
ctx, _ := signal.NotifyContext(t.Context(context.Background()), os.Interrupt)
go func() {
<-ctx.Done()
logger.Info("received shutdown signal")
t.Kill(ctx.Err())
}()
return t, ctx
}
}

// shutdowner is a fx.Invoke-compatible function that triggers an fx shutdown
// when we see that the tomb is dying.
func shutdowner(s fx.Shutdowner, tomb *tomb.Tomb, logger *zap.Logger) {
go func() {
<-tomb.Dying()
err := s.Shutdown(fx.ExitCode(0))
if err != nil {
logger.Error("shutting down", zap.Error(err))
}
}()
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/clarkmcc/brpc v0.0.0-20231108204027-edcb7338e46c // indirect
github.com/clarkmcc/brpc v0.0.0-20231123175550-0f3af44fb169 // indirect
github.com/cockroachdb/cockroach-go/v2 v2.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
Expand Down Expand Up @@ -91,4 +91,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/clarkmcc/brpc => ../brpc
//replace github.com/clarkmcc/brpc => ../brpc
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clarkmcc/brpc v0.0.0-20231108204027-edcb7338e46c h1:SAdJlSyal/8uvBiq9N8Z8SNCkdUn+NHJG2b0V72HPtk=
github.com/clarkmcc/brpc v0.0.0-20231108204027-edcb7338e46c/go.mod h1:FUkwJJi50LAKvzJv/Ibr/uOAwNWdllEu36IqEuGlANI=
github.com/clarkmcc/brpc v0.0.0-20231123175550-0f3af44fb169 h1:hLi6WXXhtU42VpZqSvavZLEHjT297mta80MU8igLK80=
github.com/clarkmcc/brpc v0.0.0-20231123175550-0f3af44fb169/go.mod h1:FUkwJJi50LAKvzJv/Ibr/uOAwNWdllEu36IqEuGlANI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
Expand Down
48 changes: 39 additions & 9 deletions internal/agent/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ import (
"gopkg.in/tomb.v2"
"io"
"sync"
"time"
)

type Client struct {
dialer func(ctx context.Context) (*brpc.ClientConn, error)
service rpc.AgentServer
logger *zap.Logger
tomb *tomb.Tomb
dialer func(ctx context.Context) (*brpc.ClientConn, error)
service rpc.AgentServer
logger *zap.Logger
tomb *tomb.Tomb
shutdown <-chan struct{}

tokenManager *tokenManager

Expand Down Expand Up @@ -165,14 +167,15 @@ func (c *Client) connectStreamsLocked(ctx context.Context) (err error) {
return nil
}
}
c.notify, err = c.agent.Notifications(ctx)
c.notify, err = c.agent.Notifications(c.shutdownCtx(ctx))
if err != nil {
return err
}
return nil
}

func (c *Client) setupClientsLocked(ctx context.Context) (err error) {
ctx = c.shutdownCtx(ctx)
c.conn, err = c.dialer(ctx)
if err != nil {
return err
Expand All @@ -181,7 +184,7 @@ func (c *Client) setupClientsLocked(ctx context.Context) (err error) {
c.agent = rpc.NewAgentManagerClient(c.conn)

c.tomb.Go(func() error {
err = brpc.ServeClientService[rpc.AgentServer](c.tomb.Dying(), c.conn, func(registrar grpc.ServiceRegistrar) {
err = brpc.ServeClientService[rpc.AgentServer](ctx.Done(), c.conn, func(registrar grpc.ServiceRegistrar) {
rpc.RegisterAgentServer(registrar, c.service)
})
if err != nil {
Expand All @@ -198,7 +201,33 @@ func (c *Client) setupClientsLocked(ctx context.Context) (err error) {
return nil
}

// shutdownCtx returns a special context that cancels once the tomb is fully
// dead. The idea here is that the client should be the last thing to shut
// down because some shutdown procedures need a working client in order to
// work (such as sending shutdown notifications).
//
// If the provided context dies, we wait for 5 seconds for the tomb to fully
// die before cancelling the return context. The idea is we:
// 1. Don't want to block forever if the provided context is cancelled but
// the tomb doesn't die.
// 2. Don't want to cancel the returned context at the same time we get the
// shutdown signal.
func (c *Client) shutdownCtx(ctx context.Context) context.Context {
ctx, cancel := context.WithCancel(context.WithoutCancel(ctx))
go func() {
select {
case <-ctx.Done():
case <-c.shutdown:
}
time.Sleep(5 * time.Second)
c.logger.Error("shutting down client context")
cancel()
}()
return ctx
}

func NewClient(
ctx context.Context,
config *Config,
tomb *tomb.Tomb,
cmd *cobra.Command,
Expand All @@ -208,9 +237,10 @@ func NewClient(
metadataProvider SystemMetadataProvider,
) *Client {
c := &Client{
tomb: tomb,
service: service,
logger: logger.Named("client"),
tomb: tomb,
service: service,
shutdown: ctx.Done(),
logger: logger.Named("client"),
dialer: func(ctx context.Context) (*brpc.ClientConn, error) {
return brpc.DialContext(ctx, config.Server.Endpoint, &tls.Config{
InsecureSkipVerify: cast.ToBool(cmd.Flag("insecure-skip-verify").Value.String()),
Expand Down
Loading

0 comments on commit 4421919

Please sign in to comment.