drone/internal/githook/env.go

119 lines
3.4 KiB
Go

// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package githook
import (
"bytes"
"encoding/base64"
"encoding/gob"
"errors"
"fmt"
"os"
)
const (
// envPayload defines the environment variable name used to send the payload to githook binary.
// NOTE: Since the variable is not meant for gitness itself, don't prefix with 'GITNESS'.
envPayload = "GIT_HOOK_GITNESS_PAYLOAD"
)
var (
ErrHookDisabled = errors.New("hook disabled")
)
// Payload defines the Payload the githook binary is initiated with when executing the git hooks.
type Payload struct {
APIBaseURL string
RepoID int64
PrincipalID int64
RequestID string
Disabled bool // this will stop processing server hooks
}
// GenerateEnvironmentVariables generates the environment variables that are sent to the githook binary.
// NOTE: for now we use a single environment variable to reduce the overal number of environment variables.
func GenerateEnvironmentVariables(payload *Payload) (map[string]string, error) {
// serialize the payload
payloadBuff := &bytes.Buffer{}
encoder := gob.NewEncoder(payloadBuff)
if err := encoder.Encode(payload); err != nil {
return nil, fmt.Errorf("failed to encode payload: %w", err)
}
// send it as base64 to avoid issues with any problematic characters
// NOTE: this will blow up the payload by ~33%, though it's not expected to be too big.
// On the other hand, we save a lot of size by only needing one environment variable name.
payloadBase64 := base64.StdEncoding.EncodeToString(payloadBuff.Bytes())
return map[string]string{
envPayload: payloadBase64,
}, nil
}
// loadPayloadFromEnvironment loads the githook payload from the environment.
func loadPayloadFromEnvironment() (*Payload, error) {
// retrieve payload from environment variables
payloadBase64, err := getEnvironmentVariable(envPayload)
if err != nil {
return nil, fmt.Errorf("failed to load payload from environment variables: %w", err)
}
// decode base64
payloadBytes, err := base64.StdEncoding.DecodeString(payloadBase64)
if err != nil {
return nil, fmt.Errorf("failed to base64 decode payload: %w", err)
}
// deserialize the payload
var payload Payload
decoder := gob.NewDecoder(bytes.NewReader(payloadBytes))
err = decoder.Decode(&payload)
if err != nil {
return nil, fmt.Errorf("failed to deserialize payload: %w", err)
}
// ensure payload is valid
err = validatePayload(&payload)
if err != nil {
return nil, fmt.Errorf("payload contains invalid data: %w", err)
}
return &payload, nil
}
// validatePayload performs a BASIC validation of the payload.
func validatePayload(payload *Payload) error {
if payload == nil {
return errors.New("payload is empty")
}
if payload.Disabled {
return ErrHookDisabled
}
if payload.APIBaseURL == "" {
return errors.New("payload doesn't contain a base url")
}
if payload.PrincipalID <= 0 {
return errors.New("payload doesn't contain a principal id")
}
if payload.RepoID <= 0 {
return errors.New("payload doesn't contain a repo id")
}
return nil
}
func getEnvironmentVariable(name string) (string, error) {
val, ok := os.LookupEnv(name)
if !ok {
return "", fmt.Errorf("'%s' not found in env", name)
}
if val == "" {
return "", fmt.Errorf("'%s' found in env but it's empty", name)
}
return val, nil
}