gitlab-org--gitlab-foss/workhorse/internal/config/config.go

235 lines
7.1 KiB
Go

package config
import (
"context"
"fmt"
"math"
"net/url"
"os"
"runtime"
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/BurntSushi/toml"
"gocloud.dev/blob"
"gocloud.dev/blob/azureblob"
"gocloud.dev/blob/gcsblob"
"gocloud.dev/gcp"
"golang.org/x/oauth2/google"
)
type TomlURL struct {
url.URL
}
func (u *TomlURL) UnmarshalText(text []byte) error {
temp, err := url.Parse(string(text))
u.URL = *temp
return err
}
type TomlDuration struct {
time.Duration
}
func (d *TomlDuration) UnmarshalText(text []byte) error {
temp, err := time.ParseDuration(string(text))
d.Duration = temp
return err
}
type ObjectStorageCredentials struct {
Provider string
S3Credentials S3Credentials `toml:"s3"`
AzureCredentials AzureCredentials `toml:"azurerm"`
GoogleCredentials GoogleCredentials `toml:"google"`
}
type ObjectStorageConfig struct {
URLMux *blob.URLMux `toml:"-"`
}
type S3Credentials struct {
AwsAccessKeyID string `toml:"aws_access_key_id"`
AwsSecretAccessKey string `toml:"aws_secret_access_key"`
}
type S3Config struct {
Region string `toml:"-"`
Bucket string `toml:"-"`
PathStyle bool `toml:"-"`
Endpoint string `toml:"-"`
UseIamProfile bool `toml:"-"`
ServerSideEncryption string `toml:"-"` // Server-side encryption mode (e.g. AES256, aws:kms)
SSEKMSKeyID string `toml:"-"` // Server-side encryption key-management service key ID (e.g. arn:aws:xxx)
}
type GoCloudConfig struct {
URL string `toml:"-"`
}
type AzureCredentials struct {
AccountName string `toml:"azure_storage_account_name"`
AccountKey string `toml:"azure_storage_access_key"`
}
type GoogleCredentials struct {
ApplicationDefault bool `toml:"google_application_default"`
JSONKeyString string `toml:"google_json_key_string"`
JSONKeyLocation string `toml:"google_json_key_location"`
}
type RedisConfig struct {
URL TomlURL
Sentinel []TomlURL
SentinelMaster string
Password string
DB *int
MaxIdle *int
MaxActive *int
}
type ImageResizerConfig struct {
MaxScalerProcs uint32 `toml:"max_scaler_procs"`
MaxFilesize uint64 `toml:"max_filesize"`
}
type TlsConfig struct {
Certificate string `toml:"certificate"`
Key string `toml:"key"`
MinVersion string `toml:"min_version"`
MaxVersion string `toml:"max_version"`
}
type ListenerConfig struct {
Network string `toml:"network"`
Addr string `toml:"addr"`
Tls *TlsConfig `toml:"tls"`
}
type Config struct {
Redis *RedisConfig `toml:"redis"`
Backend *url.URL `toml:"-"`
CableBackend *url.URL `toml:"-"`
Version string `toml:"-"`
DocumentRoot string `toml:"-"`
DevelopmentMode bool `toml:"-"`
Socket string `toml:"-"`
CableSocket string `toml:"-"`
ProxyHeadersTimeout time.Duration `toml:"-"`
APILimit uint `toml:"-"`
APIQueueLimit uint `toml:"-"`
APIQueueTimeout time.Duration `toml:"-"`
APICILongPollingDuration time.Duration `toml:"-"`
ObjectStorageConfig ObjectStorageConfig `toml:"-"`
ObjectStorageCredentials ObjectStorageCredentials `toml:"object_storage"`
PropagateCorrelationID bool `toml:"-"`
ImageResizerConfig ImageResizerConfig `toml:"image_resizer"`
AltDocumentRoot string `toml:"alt_document_root"`
ShutdownTimeout TomlDuration `toml:"shutdown_timeout"`
TrustedCIDRsForXForwardedFor []string `toml:"trusted_cidrs_for_x_forwarded_for"`
TrustedCIDRsForPropagation []string `toml:"trusted_cidrs_for_propagation"`
Listeners []ListenerConfig `toml:"listeners"`
MetricsListener *ListenerConfig `toml:"metrics_listener"`
}
var DefaultImageResizerConfig = ImageResizerConfig{
MaxScalerProcs: uint32(math.Max(2, float64(runtime.NumCPU())/2)),
MaxFilesize: 250 * 1000, // 250kB,
}
func LoadConfig(data string) (*Config, error) {
cfg := &Config{ImageResizerConfig: DefaultImageResizerConfig}
if _, err := toml.Decode(data, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (c *Config) RegisterGoCloudURLOpeners() error {
c.ObjectStorageConfig.URLMux = new(blob.URLMux)
creds := c.ObjectStorageCredentials
if strings.EqualFold(creds.Provider, "AzureRM") && creds.AzureCredentials.AccountName != "" && creds.AzureCredentials.AccountKey != "" {
urlOpener, err := creds.AzureCredentials.getURLOpener()
if err != nil {
return err
}
c.ObjectStorageConfig.URLMux.RegisterBucket(azureblob.Scheme, urlOpener)
}
if strings.EqualFold(creds.Provider, "Google") && (creds.GoogleCredentials.JSONKeyLocation != "" || creds.GoogleCredentials.JSONKeyString != "" || creds.GoogleCredentials.ApplicationDefault) {
urlOpener, err := creds.GoogleCredentials.getURLOpener()
if err != nil {
return err
}
c.ObjectStorageConfig.URLMux.RegisterBucket(gcsblob.Scheme, urlOpener)
}
return nil
}
func (creds *AzureCredentials) getURLOpener() (*azureblob.URLOpener, error) {
serviceURLOptions := azureblob.ServiceURLOptions{
AccountName: creds.AccountName,
}
clientFunc := func(svcURL azureblob.ServiceURL) (*azblob.ServiceClient, error) {
sharedKeyCred, err := azblob.NewSharedKeyCredential(creds.AccountName, creds.AccountKey)
if err != nil {
return nil, fmt.Errorf("error creating Azure credentials: %w", err)
}
return azblob.NewServiceClientWithSharedKey(string(svcURL), sharedKeyCred, &azblob.ClientOptions{})
}
return &azureblob.URLOpener{
MakeClient: clientFunc,
ServiceURLOptions: serviceURLOptions,
}, nil
}
func (creds *GoogleCredentials) getURLOpener() (*gcsblob.URLOpener, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // lint:allow context.Background
defer cancel()
gcpCredentials, err := creds.getGCPCredentials(ctx)
if err != nil {
return nil, err
}
client, err := gcp.NewHTTPClient(
gcp.DefaultTransport(),
gcp.CredentialsTokenSource(gcpCredentials),
)
if err != nil {
return nil, fmt.Errorf("error creating Google HTTP client: %w", err)
}
return &gcsblob.URLOpener{
Client: client,
}, nil
}
func (creds *GoogleCredentials) getGCPCredentials(ctx context.Context) (*google.Credentials, error) {
const gcpCredentialsScope = "https://www.googleapis.com/auth/devstorage.read_write"
if creds.ApplicationDefault {
return gcp.DefaultCredentials(ctx)
}
if creds.JSONKeyLocation != "" {
b, err := os.ReadFile(creds.JSONKeyLocation)
if err != nil {
return nil, fmt.Errorf("error reading Google json key location: %w", err)
}
return google.CredentialsFromJSON(ctx, b, gcpCredentialsScope)
}
b := []byte(creds.JSONKeyString)
return google.CredentialsFromJSON(ctx, b, gcpCredentialsScope)
}