// Package admin deals with the parsing and creation of admin.tgz files. package admin import ( "cryptic-net/garage" "cryptic-net/nebula" "cryptic-net/tarutil" "cryptic-net/yamlutil" "fmt" "io" "io/fs" "gopkg.in/yaml.v3" ) const ( creationParamsPath = "admin/creation-params.yml" nebulaCertsCACertPath = "nebula/certs/ca.crt" nebulaCertsCAKeyPath = "nebula/certs/ca.key" garageGlobalBucketKeyYmlPath = "garage/cryptic-net-global-bucket-key.yml" garageAdminBucketKeyYmlPath = "garage/cryptic-net-admin-bucket-key.yml" garageRPCSecretPath = "garage/rpc-secret.txt" ) // CreationParams are general parameters used when creating a new network. These // are available to all hosts within the network via their bootstrap files. type CreationParams struct { ID string `yaml:"id"` Domain string `yaml:"domain"` } // Admin is used for accessing all information contained within an admin.tgz. type Admin struct { CreationParams CreationParams NebulaCACredentials nebula.CACredentials GarageRPCSecret string GarageGlobalBucketS3APICredentials garage.S3APICredentials GarageAdminBucketS3APICredentials garage.S3APICredentials } // FromFS loads an Admin instance from the given fs.FS, which presumably // represents the file structure of an admin.tgz file. func FromFS(adminFS fs.FS) (Admin, error) { var a Admin filesToLoadAsYAML := []struct { into interface{} path string }{ {&a.CreationParams, creationParamsPath}, {&a.GarageGlobalBucketS3APICredentials, garageGlobalBucketKeyYmlPath}, {&a.GarageAdminBucketS3APICredentials, garageAdminBucketKeyYmlPath}, } for _, f := range filesToLoadAsYAML { if err := yamlutil.LoadYamlFSFile(f.into, adminFS, f.path); err != nil { return Admin{}, fmt.Errorf("loading %q from fs: %w", f.path, err) } } filesToLoadAsString := []struct { into *string path string }{ {&a.NebulaCACredentials.CACertPEM, nebulaCertsCACertPath}, {&a.NebulaCACredentials.CAKeyPEM, nebulaCertsCAKeyPath}, {&a.GarageRPCSecret, garageRPCSecretPath}, } for _, f := range filesToLoadAsString { body, err := fs.ReadFile(adminFS, f.path) if err != nil { return Admin{}, fmt.Errorf("loading %q from fs: %w", f.path, err) } *f.into = string(body) } return a, nil } // FromReader reads an admin.tgz file from the given io.Reader. func FromReader(r io.Reader) (Admin, error) { fs, err := tarutil.FSFromReader(r) if err != nil { return Admin{}, fmt.Errorf("reading admin.tgz: %w", err) } return FromFS(fs) } // WriteTo writes the Admin as a new admin.tgz to the given io.Writer. func (a Admin) WriteTo(into io.Writer) error { w := tarutil.NewTGZWriter(into) filesToWriteAsYAML := []struct { value interface{} path string }{ {a.CreationParams, creationParamsPath}, {a.GarageGlobalBucketS3APICredentials, garageGlobalBucketKeyYmlPath}, {a.GarageAdminBucketS3APICredentials, garageAdminBucketKeyYmlPath}, } for _, f := range filesToWriteAsYAML { b, err := yaml.Marshal(f.value) if err != nil { return fmt.Errorf("yaml encoding data for %q: %w", f.path, err) } w.WriteFileBytes(f.path, b) } filesToWriteAsString := []struct { value string path string }{ {a.NebulaCACredentials.CACertPEM, nebulaCertsCACertPath}, {a.NebulaCACredentials.CAKeyPEM, nebulaCertsCAKeyPath}, {a.GarageRPCSecret, garageRPCSecretPath}, } for _, f := range filesToWriteAsString { w.WriteFileBytes(f.path, []byte(f.value)) } return w.Close() }