Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend linting #384

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# for further options, see https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
run:
timeout: 4m
issues:
exclude-dirs:
- maintenance/errors/raven
exclude-use-default: false
include:
- EXC0005
linters:
enable:
- bodyclose
- errname
- errorlint
- forcetypeassert
- gci
# - goconst
- godot
- gofumpt
- gosec
# - ireturn
- makezero
- misspell
- nakedret
- nolintlint
- prealloc
- protogetter
- stylecheck
- unconvert
- unparam
- unused
- usestdlibvars
- whitespace
# - wrapcheck #useful but pace bricks is blocking this one (Hide)
- wsl
- zerologlint
linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/pace/bricks)
nolintlint:
allow-unused: true
stylecheck:
checks: ["all", "-ST1000"]
wsl:
allow-assign-and-call: false
allow-trailing-comment: true
force-err-cuddling: true
error-variable-names: ["err", "ok"]
10 changes: 0 additions & 10 deletions .golangci.yml

This file was deleted.

4 changes: 2 additions & 2 deletions backend/couchdb/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ func (l *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return l.transport.RoundTrip(req)
}

func (rt *AuthTransport) Transport() http.RoundTripper {
return rt.transport
func (l *AuthTransport) Transport() http.RoundTripper {
return l.transport
}

func (l *AuthTransport) SetTransport(rt http.RoundTripper) {
Expand Down
12 changes: 8 additions & 4 deletions backend/couchdb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func clientAndDB(ctx context.Context, dbName string, cfg *Config) (*kivik.Client
if db.Err() != nil {
return nil, nil, db.Err()
}

return client, db, err
}

Expand All @@ -74,6 +75,7 @@ func Client(cfg *Config) (*kivik.Client, error) {
if err != nil {
return nil, err
}

rts := []transport.ChainableRoundTripper{
&AuthTransport{
Username: cfg.User,
Expand All @@ -84,10 +86,11 @@ func Client(cfg *Config) (*kivik.Client, error) {
if !cfg.DisableRequestLogging {
rts = append(rts, &transport.LoggingRoundTripper{})
}

chain := transport.Chain(rts...)
tr := couchdb.SetTransport(chain)
err = client.Authenticate(ctx, tr)
if err != nil {

if err := client.Authenticate(ctx, tr); err != nil {
return nil, err
}

Expand All @@ -96,9 +99,10 @@ func Client(cfg *Config) (*kivik.Client, error) {

func ParseConfig() (*Config, error) {
var cfg Config
err := env.Parse(&cfg)
if err != nil {

if err := env.Parse(&cfg); err != nil {
return nil, err
}

return &cfg, nil
}
41 changes: 28 additions & 13 deletions backend/couchdb/health_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"time"

kivik "github.com/go-kivik/kivik/v3"

"github.com/pace/bricks/maintenance/health/servicehealthcheck"
"github.com/pace/bricks/maintenance/log"
)

// HealthCheck checks the state of the object storage client. It must not be changed
Expand All @@ -28,7 +30,7 @@ var (

// HealthCheck checks if the object storage client is healthy. If the last result is outdated,
// object storage is checked for upload and download,
// otherwise returns the old result
// otherwise returns the old result.
func (h *HealthCheck) HealthCheck(ctx context.Context) servicehealthcheck.HealthCheckResult {
if time.Since(h.state.LastChecked()) <= h.Config.HealthCheckResultTTL {
// the last health check is not outdated, an can be reused.
Expand All @@ -38,14 +40,16 @@ func (h *HealthCheck) HealthCheck(ctx context.Context) servicehealthcheck.Health
checkTime := time.Now()

var doc Doc

var err error

var row *kivik.Row

check:
// check if context was canceled
select {
case <-ctx.Done():
h.state.SetErrorState(fmt.Errorf("failed: %v", ctx.Err()))
h.state.SetErrorState(fmt.Errorf("failed: %w", ctx.Err()))
return h.state.GetState()
default:
}
Expand All @@ -55,16 +59,22 @@ check:
if kivik.StatusCode(row.Err) == http.StatusNotFound {
goto put
}
h.state.SetErrorState(fmt.Errorf("failed to get: %#v", row.Err))

h.state.SetErrorState(fmt.Errorf("failed to get: %w", row.Err))

return h.state.GetState()
}
defer row.Body.Close()

defer func() {
if err := row.Body.Close(); err != nil {
log.Ctx(ctx).Debug().Err(err).Msg("Failed closing body")
}
}()

// check if document exists
if row.Rev != "" {
err = row.ScanDoc(&doc)
if err != nil {
h.state.SetErrorState(fmt.Errorf("failed to get: %v", row.Err))
if err := row.ScanDoc(&doc); err != nil {
h.state.SetErrorState(fmt.Errorf("failed to get: %w", row.Err))
return h.state.GetState()
}

Expand All @@ -77,23 +87,27 @@ check:
put:
// update document
doc.ID = h.Config.HealthCheckKey

doc.Time = time.Now().Format(healthCheckTimeFormat)

_, err = h.DB.Put(ctx, h.Config.HealthCheckKey, doc)
if err != nil {
// not yet created, try to create
if h.Config.DatabaseAutoCreate && kivik.StatusCode(err) == http.StatusNotFound {
err := h.Client.CreateDB(ctx, h.Name)
if err != nil {
h.state.SetErrorState(fmt.Errorf("failed to put object: %v", err))
if err := h.Client.CreateDB(ctx, h.Name); err != nil {
h.state.SetErrorState(fmt.Errorf("failed to put object: %w", err))
return h.state.GetState()
}

goto put
}

if kivik.StatusCode(err) == http.StatusConflict {
goto check
}
h.state.SetErrorState(fmt.Errorf("failed to put object: %v", err))

h.state.SetErrorState(fmt.Errorf("failed to put object: %w", err))

return h.state.GetState()
}

Expand All @@ -103,6 +117,7 @@ put:
healthy:
// If uploading and downloading worked set the Health Check to healthy
h.state.SetHealthy()

return h.state.GetState()
}

Expand All @@ -116,15 +131,15 @@ type Doc struct {
// time span concurrent request to the objstore may break the assumption
// that the value is the same, but in this case it would be acceptable.
// Assumption all instances are created equal and one providing evidence
// of a good write would be sufficient. See #244
// of a good write would be sufficient. See #244.
func wasConcurrentHealthCheck(checkTime time.Time, observedValue string) bool {
t, err := time.Parse(healthCheckTimeFormat, observedValue)
if err == nil {
allowedStart := checkTime.Add(-healthCheckConcurrentSpan)
allowedEnd := checkTime.Add(healthCheckConcurrentSpan)

// timestamp we got from the document is in allowed range
// concider it healthy
// consider it healthy
return t.After(allowedStart) && t.Before(allowedEnd)
}

Expand Down
44 changes: 29 additions & 15 deletions backend/k8sapi/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,76 +15,84 @@ import (
"strings"

"github.com/caarlos0/env/v10"

"github.com/pace/bricks/http/transport"
"github.com/pace/bricks/maintenance/log"
)

// Client minimal client for the kubernetes API
// Client minimal client for the kubernetes API.
type Client struct {
Podname string
Namespace string
CACert []byte
Token string
cfg Config
HttpClient *http.Client
HTTPClient *http.Client
}

// NewClient create new api client
// NewClient create new api client.
func NewClient() (*Client, error) {
cl := Client{
HttpClient: &http.Client{},
HTTPClient: &http.Client{},
}

// lookup hostname (for pod update)
hostname, err := os.Hostname()
if err != nil {
return nil, err
}

cl.Podname = hostname

// parse environment including secrets mounted by kubernetes
err = env.Parse(&cl.cfg)
if err != nil {
if err := env.Parse(&cl.cfg); err != nil {
return nil, err
}

caData, err := os.ReadFile(cl.cfg.CACertFile)
if err != nil {
return nil, fmt.Errorf("failed to read %q: %v", cl.cfg.CACertFile, err)
return nil, fmt.Errorf("failed to read %q: %w", cl.cfg.CACertFile, err)
}

cl.CACert = []byte(strings.TrimSpace(string(caData)))

namespaceData, err := os.ReadFile(cl.cfg.NamespaceFile)
if err != nil {
return nil, fmt.Errorf("failed to read %q: %v", cl.cfg.NamespaceFile, err)
return nil, fmt.Errorf("failed to read %q: %w", cl.cfg.NamespaceFile, err)
}

cl.Namespace = strings.TrimSpace(string(namespaceData))

tokenData, err := os.ReadFile(cl.cfg.TokenFile)
if err != nil {
return nil, fmt.Errorf("failed to read %q: %v", cl.cfg.CACertFile, err)
return nil, fmt.Errorf("failed to read %q: %w", cl.cfg.CACertFile, err)
}

cl.Token = strings.TrimSpace(string(tokenData))

// add kubernetes api server cert
chain := transport.NewDefaultTransportChain()
pool := x509.NewCertPool()

ok := pool.AppendCertsFromPEM(cl.CACert)
if !ok {
return nil, fmt.Errorf("failed to load kubernetes ca cert")
}

chain.Final(&http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
RootCAs: pool,
MinVersion: tls.VersionTLS12,
},
})
cl.HttpClient.Transport = chain

cl.HTTPClient.Transport = chain

return &cl, nil
}

// SimpleRequest send a simple http request to kubernetes with the passed
// method, url and requestObj, decoding the result into responseObj
// method, url and requestObj, decoding the result into responseObj.
func (c *Client) SimpleRequest(ctx context.Context, method, url string, requestObj, responseObj interface{}) error {
data, err := json.Marshal(requestObj)
if err != nil {
Expand All @@ -99,16 +107,22 @@ func (c *Client) SimpleRequest(ctx context.Context, method, url string, requestO
req.Header.Set("Content-Type", "application/json-patch+json")
req.Header.Set("Authorization", "Bearer "+c.Token)

resp, err := c.HttpClient.Do(req)
resp, err := c.HTTPClient.Do(req)
if err != nil {
log.Ctx(ctx).Debug().Err(err).Msg("failed to do api request")
return err
}
defer resp.Body.Close()

defer func() {
if err := resp.Body.Close(); err != nil {
log.Ctx(ctx).Debug().Err(err).Msg("failed to close response body")
}
}()

if resp.StatusCode > 299 {
body, _ := io.ReadAll(resp.Body) // nolint: errcheck
body, _ := io.ReadAll(resp.Body)
log.Ctx(ctx).Debug().Msgf("failed to do api request, due to: %s", string(body))

return fmt.Errorf("k8s request failed with %s", resp.Status)
}

Expand Down
2 changes: 1 addition & 1 deletion backend/k8sapi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
package k8sapi

// Config gathers the required kubernetes system configuration to use the
// kubernetes API
// kubernetes API.
type Config struct {
Host string `env:"KUBERNETES_SERVICE_HOST" envDefault:"localhost"`
Port int `env:"KUBERNETES_PORT_443_TCP_PORT" envDefault:"433"`
Expand Down
5 changes: 3 additions & 2 deletions backend/k8sapi/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (
)

// SetCurrentPodLabel set the label for the current pod in the current
// namespace (requires patch on pods resource)
// namespace (requires patch on pods resource).
func (c *Client) SetCurrentPodLabel(ctx context.Context, label, value string) error {
return c.SetPodLabel(ctx, c.Namespace, c.Podname, label, value)
}

// SetPodLabel sets the label and value for the pod of the given namespace
// (requires patch on pods resource in the given namespace)
// (requires patch on pods resource in the given namespace).
func (c *Client) SetPodLabel(ctx context.Context, namespace, podname, label, value string) error {
pr := []struct {
Op string `json:"op"`
Expand All @@ -30,6 +30,7 @@ func (c *Client) SetPodLabel(ctx context.Context, namespace, podname, label, val
}
url := fmt.Sprintf("https://%s:%d/api/v1/namespaces/%s/pods/%s",
c.cfg.Host, c.cfg.Port, namespace, podname)

var resp interface{}

return c.SimpleRequest(ctx, http.MethodPatch, url, &pr, &resp)
Expand Down
Loading
Loading