aptly/utils/config.go

319 lines
11 KiB
Go

package utils
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/DisposaBoy/JsonConfigReader"
"gopkg.in/yaml.v3"
)
// ConfigStructure is structure of main configuration
type ConfigStructure struct { // nolint: maligned
// General
RootDir string `json:"rootDir" yaml:"root_dir"`
LogLevel string `json:"logLevel" yaml:"log_level"`
LogFormat string `json:"logFormat" yaml:"log_format"`
DatabaseOpenAttempts int `json:"databaseOpenAttempts" yaml:"database_open_attempts"`
Architectures []string `json:"architectures" yaml:"architectures"`
SkipLegacyPool bool `json:"skipLegacyPool" yaml:"skip_legacy_pool"` // OBSOLETE
// Dependency following
DepFollowSuggests bool `json:"dependencyFollowSuggests" yaml:"dep_follow_suggests"`
DepFollowRecommends bool `json:"dependencyFollowRecommends" yaml:"dep_follow_recommends"`
DepFollowAllVariants bool `json:"dependencyFollowAllVariants" yaml:"dep_follow_all_variants"`
DepFollowSource bool `json:"dependencyFollowSource" yaml:"dep_follow_source"`
DepVerboseResolve bool `json:"dependencyVerboseResolve" yaml:"dep_verboseresolve"`
// PPA
PpaDistributorID string `json:"ppaDistributorID" yaml:"ppa_distributor_id"`
PpaCodename string `json:"ppaCodename" yaml:"ppa_codename"`
// Server
ServeInAPIMode bool `json:"serveInAPIMode" yaml:"serve_in_api_mode"`
EnableMetricsEndpoint bool `json:"enableMetricsEndpoint" yaml:"enable_metrics_endpoint"`
EnableSwaggerEndpoint bool `json:"enableSwaggerEndpoint" yaml:"enable_swagger_endpoint"`
AsyncAPI bool `json:"AsyncAPI" yaml:"async_api"` // OBSOLETE
// Database
DatabaseBackend DBConfig `json:"databaseBackend" yaml:"database_backend"`
// Mirroring
Downloader string `json:"downloader" yaml:"downloader"`
DownloadConcurrency int `json:"downloadConcurrency" yaml:"download_concurrency"`
DownloadLimit int64 `json:"downloadSpeedLimit" yaml:"download_limit"`
DownloadRetries int `json:"downloadRetries" yaml:"download_retries"`
DownloadSourcePackages bool `json:"downloadSourcePackages" yaml:"download_sourcepackages"`
// Signing
GpgProvider string `json:"gpgProvider" yaml:"gpg_provider"`
GpgDisableSign bool `json:"gpgDisableSign" yaml:"gpg_disable_sign"`
GpgDisableVerify bool `json:"gpgDisableVerify" yaml:"gpg_disable_verify"`
// Publishing
SkipContentsPublishing bool `json:"skipContentsPublishing" yaml:"skip_contents_publishing"`
SkipBz2Publishing bool `json:"skipBz2Publishing" yaml:"skip_bz2_publishing"`
// Storage
FileSystemPublishRoots map[string]FileSystemPublishRoot `json:"FileSystemPublishEndpoints" yaml:"filesystem_publish_endpoints"`
S3PublishRoots map[string]S3PublishRoot `json:"S3PublishEndpoints" yaml:"s3_publish_endpoints"`
SwiftPublishRoots map[string]SwiftPublishRoot `json:"SwiftPublishEndpoints" yaml:"swift_publish_endpoints"`
AzurePublishRoots map[string]AzureEndpoint `json:"AzurePublishEndpoints" yaml:"azure_publish_endpoints"`
PackagePoolStorage PackagePoolStorage `json:"packagePoolStorage" yaml:"packagepool_storage"`
}
// DBConfig
type DBConfig struct {
Type string `json:"type" yaml:"type"`
DbPath string `json:"dbPath" yaml:"db_path"`
URL string `json:"url" yaml:"url"`
}
type LocalPoolStorage struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
}
type PackagePoolStorage struct {
Local *LocalPoolStorage
Azure *AzureEndpoint
}
var AZURE = "azure"
var LOCAL = "local"
func (pool *PackagePoolStorage) UnmarshalJSON(data []byte) error {
var discriminator struct {
Type string `json:"type"`
}
if err := json.Unmarshal(data, &discriminator); err != nil {
return err
}
switch discriminator.Type {
case AZURE:
pool.Azure = &AzureEndpoint{}
return json.Unmarshal(data, &pool.Azure)
case LOCAL, "":
pool.Local = &LocalPoolStorage{}
return json.Unmarshal(data, &pool.Local)
default:
return fmt.Errorf("unknown pool storage type: %s", discriminator.Type)
}
}
func (pool *PackagePoolStorage) UnmarshalYAML(unmarshal func(interface{}) error) error {
var discriminator struct {
Type string `yaml:"type"`
}
if err := unmarshal(&discriminator); err != nil {
return err
}
switch discriminator.Type {
case AZURE:
pool.Azure = &AzureEndpoint{}
return unmarshal(&pool.Azure)
case LOCAL, "":
pool.Local = &LocalPoolStorage{}
return unmarshal(&pool.Local)
default:
return fmt.Errorf("unknown pool storage type: %s", discriminator.Type)
}
}
func (pool *PackagePoolStorage) MarshalJSON() ([]byte, error) {
var wrapper struct {
Type string `json:"type,omitempty"`
*LocalPoolStorage
*AzureEndpoint
}
if pool.Azure != nil {
wrapper.Type = "azure"
wrapper.AzureEndpoint = pool.Azure
} else if pool.Local.Path != "" {
wrapper.Type = "local"
wrapper.LocalPoolStorage = pool.Local
}
return json.Marshal(wrapper)
}
func (pool PackagePoolStorage) MarshalYAML() (interface{}, error) {
var wrapper struct {
Type string `yaml:"type,omitempty"`
*LocalPoolStorage `yaml:",inline"`
*AzureEndpoint `yaml:",inline"`
}
if pool.Azure != nil {
wrapper.Type = "azure"
wrapper.AzureEndpoint = pool.Azure
} else if pool.Local.Path != "" {
wrapper.Type = "local"
wrapper.LocalPoolStorage = pool.Local
}
return wrapper, nil
}
// FileSystemPublishRoot describes single filesystem publishing entry point
type FileSystemPublishRoot struct {
RootDir string `json:"rootDir" yaml:"root_dir"`
LinkMethod string `json:"linkMethod" yaml:"link_method"`
VerifyMethod string `json:"verifyMethod" yaml:"verify_method"`
}
// S3PublishRoot describes single S3 publishing entry point
type S3PublishRoot struct {
Region string `json:"region" yaml:"region"`
Bucket string `json:"bucket" yaml:"bucket"`
Prefix string `json:"prefix" yaml:"prefix"`
ACL string `json:"acl" yaml:"acl"`
AccessKeyID string `json:"awsAccessKeyID" yaml:"access_key_id"`
SecretAccessKey string `json:"awsSecretAccessKey" yaml:"secret_access_key"`
SessionToken string `json:"awsSessionToken" yaml:"session_token"`
Endpoint string `json:"endpoint" yaml:"endpoint"`
StorageClass string `json:"storageClass" yaml:"storage_class"`
EncryptionMethod string `json:"encryptionMethod" yaml:"encryption_method"`
PlusWorkaround bool `json:"plusWorkaround" yaml:"plus_workaround"`
DisableMultiDel bool `json:"disableMultiDel" yaml:"disable_multidel"`
ForceSigV2 bool `json:"forceSigV2" yaml:"force_sigv2"`
ForceVirtualHostedStyle bool `json:"forceVirtualHostedStyle" yaml:"force_virtualhosted_style"`
Debug bool `json:"debug" yaml:"debug"`
}
// SwiftPublishRoot describes single OpenStack Swift publishing entry point
type SwiftPublishRoot struct {
Container string `json:"container" yaml:"container"`
Prefix string `json:"prefix" yaml:"prefix"`
UserName string `json:"osname" yaml:"username"`
Password string `json:"password" yaml:"password"`
Tenant string `json:"tenant" yaml:"tenant"`
TenantID string `json:"tenantid" yaml:"tenant_id"`
Domain string `json:"domain" yaml:"domain"`
DomainID string `json:"domainid" yaml:"domain_id"`
TenantDomain string `json:"tenantdomain" yaml:"tenant_domain"`
TenantDomainID string `json:"tenantdomainid" yaml:"tenant_domain_id"`
AuthURL string `json:"authurl" yaml:"auth_url"`
}
// AzureEndpoint describes single Azure publishing entry point
type AzureEndpoint struct {
Container string `json:"container" yaml:"container"`
Prefix string `json:"prefix" yaml:"prefix"`
AccountName string `json:"accountName" yaml:"account_name"`
AccountKey string `json:"accountKey" yaml:"account_key"`
Endpoint string `json:"endpoint" yaml:"endpoint"`
}
// Config is configuration for aptly, shared by all modules
var Config = ConfigStructure{
RootDir: filepath.Join(os.Getenv("HOME"), ".aptly"),
DownloadConcurrency: 4,
DownloadLimit: 0,
Downloader: "default",
DatabaseOpenAttempts: -1,
Architectures: []string{},
DepFollowSuggests: false,
DepFollowRecommends: false,
DepFollowAllVariants: false,
DepFollowSource: false,
GpgProvider: "gpg",
GpgDisableSign: false,
GpgDisableVerify: false,
DownloadSourcePackages: false,
PackagePoolStorage: PackagePoolStorage{
Local: &LocalPoolStorage{Path: ""},
},
SkipLegacyPool: false,
PpaDistributorID: "ubuntu",
PpaCodename: "",
FileSystemPublishRoots: map[string]FileSystemPublishRoot{},
S3PublishRoots: map[string]S3PublishRoot{},
SwiftPublishRoots: map[string]SwiftPublishRoot{},
AzurePublishRoots: map[string]AzureEndpoint{},
AsyncAPI: false,
EnableMetricsEndpoint: false,
LogLevel: "info",
LogFormat: "default",
ServeInAPIMode: false,
EnableSwaggerEndpoint: false,
}
// LoadConfig loads configuration from json file
func LoadConfig(filename string, config *ConfigStructure) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
decJSON := json.NewDecoder(JsonConfigReader.New(f))
if err = decJSON.Decode(&config); err != nil {
f.Seek(0, 0)
decYAML := yaml.NewDecoder(f)
if err2 := decYAML.Decode(&config); err2 != nil {
err = fmt.Errorf("invalid yaml (%s) or json (%s)", err2, err)
} else {
err = nil
}
}
return err
}
// SaveConfig write configuration to json file
func SaveConfig(filename string, config *ConfigStructure) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
encoded, err := json.MarshalIndent(&config, "", " ")
if err != nil {
return err
}
_, err = f.Write(encoded)
return err
}
// SaveConfigRaw write configuration to file
func SaveConfigRaw(filename string, conf []byte) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = f.Write(conf)
return err
}
// SaveConfigYAML write configuration to yaml file
func SaveConfigYAML(filename string, config *ConfigStructure) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
yamlData, err := yaml.Marshal(&config)
if err != nil {
return fmt.Errorf("error marshaling to YAML: %s", err)
}
_, err = f.Write(yamlData)
return err
}
// GetRootDir returns the RootDir with expanded ~ as home directory
func (conf *ConfigStructure) GetRootDir() string {
return strings.Replace(conf.RootDir, "~", os.Getenv("HOME"), 1)
}