Skip to content

Commit

Permalink
feat: added base repo scaffolding (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
shanduur authored Nov 24, 2023
1 parent 805f07a commit 905538a
Show file tree
Hide file tree
Showing 23 changed files with 1,362 additions and 1 deletion.
23 changes: 23 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; https://editorconfig.org/

root = true

[*]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
indent_size = 4
indent_style = tab

[*.md]
indent_size = 4
trim_trailing_whitespace = false

eclint_indent_style = unset

[Dockerfile]
indent_size = 4
92 changes: 92 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
run:
# Timeout for analysis, e.g. 30s, 5m.
timeout: 5m

# Include test files.
tests: true

issues:
# Set to 0 to not skip any issues.
max-issues-per-linter: 0

# Set to 0 to not skip any issues.
max-same-issues: 0

output:
# Sort results by: filepath, then line, then column.
sort-results: true

# Make issues output unique by line.
uniq-by-line: false

linters:
# Enable specific linter
enable:
# Detect context.Context contained in structs.
- containedctx
# Check whether a function uses a non-inherited context.
- contextcheck
# Find declarations and assignments with too many blank identifiers.
- dogsled
# Check for unchecked errors.
- errcheck
# Find code that will cause problems with the error wrapping scheme.
- errorlint
# Find exporting pointers for loop variables.
- exportloopref
# Inspects source code for security problems.
- gosec
# Check that compiler directives are valid.
- gocheckcompilerdirectives
# Calculate cognitive complexities of functions.
- gocognit
# Find repeated strings that could be replaced by a constant.
- goconst
# Provides functionalities missing from other linters.
- gocritic
# Calculates cyclomatic complexity of a function.
- gocyclo
# Check if comments end with a dot.
- godot
# A stricter replacement for gofmt.
- gofumpt
# GO Magic Number Detector.
- gomnd
# Simplify the code.
- gosimple
# Check for correctness of programs.
- govet
# Detect ineffectual assignments.
- ineffassign
# Correct commonly misspelled English words in source files.
- misspell
# Finds the code that returns nil even if it checks that the error is not nil.
- nilerr
# Checks that there is no simultaneous return of nil error and an invalid value.
- nilnil
# Find incorrect usages of t.Parallel().
- paralleltest
# Reports direct reads from proto message fields when getters should be used.
- protogetter
# Drop-in replacement of golint.
- revive
# Ensure consistent code style when using log/slog.
- sloglint
# Find bugs and performance issues statically.
- staticcheck
# Checks Go code for unused constants, variables, functions and types.
- unused
# Empty lines linter.
- wsl

# Setting of specific linters.
linters-settings:
paralleltest:
# Ignore missing calls to `t.Parallel()` and only report incorrect uses of it.
ignore-missing: false

sloglint:
# Enforce using key-value pairs only (incompatible with attr-only).
kv-only: true
# Enforce a single key naming convention.
key-naming-case: snake
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2023 Linode, LLC
Copyright 2023 Akamai Technologies, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
75 changes: 75 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright 2023 Akamai Technologies, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

SHELL := /usr/bin/env bash -o errexit -o pipefail -o nounset
.DEFAULT_GOAL := help

GO ?= go
ENGINE ?= docker

VERSION ?= $(shell git rev-parse HEAD)
TOOLCHAIN_VERSION := $(shell sed -En 's/^go (.*)$$/\1/p' go.mod)

REGISTRY := docker.io
IMAGE := linode/linode-cosi-driver

CONTAINERFILE ?= Dockerfile
OCI_TAGS += --tag=${REGISTRY}/${IMAGE}:${VERSION}
OCI_BUILDARGS += --build-arg=VERSION=${VERSION}

LDFLAGS ?=
GOFLAGS ?=
GO_SETTINGS += CGO_ENABLED=0

.PHONY: all
all: test build image # Run all targets.

.PHONY: build
build: clean # Build the binary.
${GO_SETTINGS} ${GO} build \
${GOFLAGS} \
-ldflags="${LDFLAGS}" \
-o ./bin/${NAME} \
./cmd/linode-cosi-driver

.PHONY: image
image: clean-image # Build container image.
${ENGINE} build \
${OCI_TAGS} ${OCI_BUILDARGS} \
--file=${CONTAINERFILE} \
--target=runtime \
.

.PHONY: test
test: # Run unit tests.
${GO} test ${GOFLAGS} \
-race \
-cover -covermode=atomic -coverprofile=coverage.out \
./...

.PHONY: e2e
e2e: # Run end to end tests. (FIXME: this is placeholder)
@-echo "this is placeholder"

.PHONY: clean
clean: # Clean the previous build files.
@rm -rf ./bin

.PHONY: clean-image
clean-image: # Attempt to remove the old container image builds.
@-${ENGINE} image rm -f $(shell ${ENGINE} image ls -aq ${REGISTRY}/${REPOSITORY}/${NAME}:${VERSION} | xargs -n1 | sort -u | xargs)

.PHONY: help
help: # Show help for each of the Makefile recipes.
@grep -E '^[a-zA-Z0-9 -]+:.*#' Makefile | while read -r l; do printf "\033[1;32m$$(echo $$l | cut -f 1 -d':')\033[00m:$$(echo $$l | cut -f 2- -d'#')\n"; done
164 changes: 164 additions & 0 deletions cmd/linode-cosi-driver/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright 2023 Akamai Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"errors"
"fmt"
"log/slog"
"net/url"
"os"
"os/signal"
"sync"
"syscall"
"time"

"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/linode/linode-cosi-driver/pkg/endpoint"
"github.com/linode/linode-cosi-driver/pkg/envflag"
"github.com/linode/linode-cosi-driver/pkg/grpc/handlers"
"github.com/linode/linode-cosi-driver/pkg/grpc/logger"
"github.com/linode/linode-cosi-driver/pkg/servers/identity"
"github.com/linode/linode-cosi-driver/pkg/servers/provisioner"
"google.golang.org/grpc"
cosi "sigs.k8s.io/container-object-storage-interface-spec"
)

var log *slog.Logger

const (
driverName = "objectstorage.cosi.linode.com"
gracePeriod = 5 * time.Second
)

func main() {
linodeToken := envflag.String("LINODE_TOKEN", "")
linodeURL := envflag.String("LINODE_API_URL", "")
cosiEndpoint := envflag.String("COSI_ENDPOINT", "unix:///var/lib/cosi/cosi.sock")

// TODO: any logger settup must be done here, before first log call.
log = slog.Default()

if err := realMain(context.Background(), cosiEndpoint, linodeToken, linodeURL); err != nil {
slog.Error("critical failure", "error", err)
os.Exit(1)
}
}

func realMain(ctx context.Context, cosiEndpoint, _, _ string) error {
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer stop()

// create identity server
idSrv, err := identity.New(driverName)
if err != nil {
return fmt.Errorf("failed to create identity server: %w", err)
}

// create provisioner server
prvSrv, err := provisioner.New(log)
if err != nil {
return fmt.Errorf("failed to create provisioner server: %w", err)
}

// parse endpoint
endpointURL, err := url.Parse(cosiEndpoint)
if err != nil {
return fmt.Errorf("unable to parse COSI endpoint: %w", err)
}

// create the endpoint handler
lis, err := endpoint.New(endpointURL).Listener(ctx)
if err != nil {
return fmt.Errorf("unable to create new listener: %w", err)
}
defer lis.Close()

// create the grpcServer
srv, err := grpcServer(ctx, idSrv, prvSrv)
if err != nil {
return fmt.Errorf("gRPC server creation failed: %w", err)
}

var wg sync.WaitGroup

wg.Add(1)

go shutdown(ctx, &wg, srv)

slog.Info("starting server", "endpoint", endpointURL)

err = srv.Serve(lis)
if err != nil {
return fmt.Errorf("gRPC server failed: %w", err)
}

wg.Wait()

return nil
}

func grpcServer(ctx context.Context, identity cosi.IdentityServer, provisioner cosi.ProvisionerServer) (*grpc.Server, error) {
server := grpc.NewServer(
grpc.ChainUnaryInterceptor(
logging.UnaryServerInterceptor(logger.Wrap(log)),
recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(handlers.PanicRecovery(ctx, log))),
),
)

if identity == nil || provisioner == nil {
return nil, errors.New("provisioner and identity servers cannot be nil")
}

cosi.RegisterIdentityServer(server, identity)
cosi.RegisterProvisionerServer(server, provisioner)

return server, nil
}

// shutdown handles shutdown with grace period consideration.
func shutdown(ctx context.Context, wg *sync.WaitGroup, g *grpc.Server) {
<-ctx.Done()
defer wg.Done()
defer slog.Info("stopped")

slog.Info("shutting down")

dctx, stop := context.WithTimeout(context.Background(), gracePeriod)
defer stop()

c := make(chan struct{})

if g != nil {
go func() {
g.GracefulStop()
c <- struct{}{}
}()

for {
select {
case <-dctx.Done():
slog.Warn("forcing shutdown")
g.Stop()

return
case <-c:
return
}
}
}
}
31 changes: 31 additions & 0 deletions cmd/linode-cosi-driver/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2023 Akamai Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main_test

import "testing"

func TestRealMain(t *testing.T) {
t.Parallel()

for _, tc := range []struct {
testName string
}{} {
tc := tc

t.Run(tc.testName, func(t *testing.T) {
t.Parallel()
})
}
}
Loading

0 comments on commit 905538a

Please sign in to comment.