diff --git a/environment/api.go b/environment/api.go index 4884b091..b0bce452 100644 --- a/environment/api.go +++ b/environment/api.go @@ -2,12 +2,13 @@ package environment import ( "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/environment/env_v0_3x" "github.com/openziti/zrok/rest_client_zrok" + "github.com/pkg/errors" ) type Root interface { Metadata() *env_core.Metadata - IsLatest() bool HasConfig() (bool, error) Config() *env_core.Config SetConfig(cfg *env_core.Config) error @@ -27,11 +28,24 @@ func ListRoots() ([]*env_core.Metadata, error) { } func LoadRoot() (Root, error) { - return nil, nil + return env_v0_3x.Load() } func LoadRootVersion(m *env_core.Metadata) (Root, error) { - return nil, nil + if m == nil { + return nil, errors.Errorf("specify metadata version") + } + switch m.V { + case env_v0_3x.V: + return env_v0_3x.Load() + + default: + return nil, errors.Errorf("unknown metadata version '%v'", m.V) + } +} + +func NeedsUpdate(r Root) bool { + return r.Metadata().V != env_v0_3x.V } func UpdateRoot(r Root) (Root, error) { diff --git a/environment/env_v0_3x/api.go b/environment/env_v0_3x/api.go new file mode 100644 index 00000000..fb19186b --- /dev/null +++ b/environment/env_v0_3x/api.go @@ -0,0 +1,65 @@ +package env_v0_3x + +import ( + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/rest_client_zrok" +) + +func (r *Root) Metadata() *env_core.Metadata { + return r.meta +} + +func (r *Root) HasConfig() (bool, error) { + return r.cfg != nil, nil +} + +func (r *Root) Config() *env_core.Config { + return r.cfg +} + +func (r *Root) SetConfig(cfg *env_core.Config) error { + if err := saveConfig(cfg); err != nil { + return err + } + r.cfg = cfg + return nil +} + +func (r *Root) Client() (*rest_client_zrok.Zrok, error) { + return nil, nil +} + +func (r *Root) ApiEndpoint() (string, string) { + if r.env != nil { + return r.env.ApiEndpoint, "env" + } + return "", "" +} + +func (r *Root) Environment() *env_core.Environment { + return r.env +} + +func (r *Root) DeleteEnvironment() error { + return nil +} + +func (r *Root) IsEnabled() (bool, error) { + return r.env != nil, nil +} + +func (r *Root) ZitiIdentityFile(name string) (string, error) { + return "", nil +} + +func (r *Root) SaveZitiIdentity(name, data string) error { + return nil +} + +func (r *Root) DeleteZitiIdentity(name string) error { + return nil +} + +func (r *Root) Obliterate() error { + return nil +} diff --git a/environment/env_v0_3x/dirs.go b/environment/env_v0_3x/dirs.go new file mode 100644 index 00000000..10a70de7 --- /dev/null +++ b/environment/env_v0_3x/dirs.go @@ -0,0 +1,38 @@ +package env_v0_3x + +import ( + "os" + "path/filepath" +) + +func rootDir() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".zrok"), nil +} + +func metadataFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "metadata.json"), nil +} + +func configFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "config.json"), nil +} + +func environmentFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "environment.json"), nil +} diff --git a/environment/env_v0_3x/root.go b/environment/env_v0_3x/root.go new file mode 100644 index 00000000..2a725376 --- /dev/null +++ b/environment/env_v0_3x/root.go @@ -0,0 +1,216 @@ +package env_v0_3x + +import ( + "encoding/json" + "github.com/openziti/zrok/environment/env_core" + "github.com/pkg/errors" + "os" + "path/filepath" +) + +const V = "v0.3" + +type Root struct { + meta *env_core.Metadata + cfg *env_core.Config + env *env_core.Environment +} + +func Load() (*Root, error) { + r := &Root{} + exists, err := rootExists() + if err != nil { + return nil, err + } + if exists { + if meta, err := loadMetadata(); err == nil { + r.meta = meta + } else { + return nil, err + } + + if cfg, err := loadConfig(); err == nil { + r.cfg = cfg + } + + if env, err := loadEnvironment(); err == nil { + r.env = env + } + + } else { + root, err := rootDir() + if err != nil { + return nil, err + } + r.meta = &env_core.Metadata{ + V: V, + RootPath: root, + } + } + return r, nil +} + +func rootExists() (bool, error) { + mf, err := metadataFile() + if err != nil { + return false, err + } + _, err = os.Stat(mf) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, err +} + +func loadMetadata() (*env_core.Metadata, error) { + mf, err := metadataFile() + if err != nil { + return nil, err + } + data, err := os.ReadFile(mf) + if err != nil { + return nil, err + } + m := &metadata{} + if err := json.Unmarshal(data, m); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling metadata file '%v'", mf) + } + if m.V != V { + return nil, errors.Errorf("got metadata version '%v', expected '%v'", m.V, V) + } + rf, err := rootDir() + if err != nil { + return nil, err + } + out := &env_core.Metadata{ + V: m.V, + RootPath: rf, + } + return out, nil +} + +func loadConfig() (*env_core.Config, error) { + cf, err := configFile() + if err != nil { + return nil, errors.Wrap(err, "error getting config file path") + } + data, err := os.ReadFile(cf) + if err != nil { + return nil, errors.Wrapf(err, "error reading config file '%v'", cf) + } + cfg := &config{} + if err := json.Unmarshal(data, cfg); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling config file '%v'", cf) + } + out := &env_core.Config{ + ApiEndpoint: cfg.ApiEndpoint, + } + return out, nil +} + +func saveConfig(cfg *env_core.Config) error { + in := &config{ApiEndpoint: cfg.ApiEndpoint} + data, err := json.MarshalIndent(in, "", " ") + if err != nil { + return errors.Wrap(err, "error marshaling config") + } + cf, err := configFile() + if err != nil { + return errors.Wrap(err, "error getting config file path") + } + if err := os.MkdirAll(filepath.Dir(cf), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(cf)) + } + if err := os.WriteFile(cf, data, os.FileMode(0600)); err != nil { + return errors.Wrap(err, "error saving config file") + } + return nil +} + +func isEnabled() (bool, error) { + ef, err := environmentFile() + if err != nil { + return false, errors.Wrap(err, "error getting environment file path") + } + _, err = os.Stat(ef) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, errors.Wrapf(err, "error stat-ing environment file '%v'", ef) + } + return true, nil +} + +func loadEnvironment() (*env_core.Environment, error) { + ef, err := environmentFile() + if err != nil { + return nil, errors.Wrap(err, "error getting environment file") + } + data, err := os.ReadFile(ef) + if err != nil { + return nil, errors.Wrapf(err, "error reading environment file '%v'", ef) + } + env := &environment{} + if err := json.Unmarshal(data, env); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling environment file '%v'", ef) + } + out := &env_core.Environment{ + Token: env.Token, + ZitiIdentity: env.ZId, + ApiEndpoint: env.ApiEndpoint, + } + return out, nil +} + +func saveEnvironment(env *env_core.Environment) error { + in := &environment{ + Token: env.Token, + ZId: env.ZitiIdentity, + ApiEndpoint: env.ApiEndpoint, + } + data, err := json.MarshalIndent(in, "", " ") + if err != nil { + return errors.Wrap(err, "error marshaling environment") + } + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.MkdirAll(filepath.Dir(ef), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(ef)) + } + if err := os.WriteFile(ef, data, os.FileMode(0600)); err != nil { + return errors.Wrap(err, "error saving environment file") + } + return nil +} + +func deleteEnvironment() error { + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.Remove(ef); err != nil { + return errors.Wrap(err, "error removing environment file") + } + + return nil +} + +type metadata struct { + V string `json:"v"` +} + +type config struct { + ApiEndpoint string `json:"api_endpoint"` +} + +type environment struct { + Token string `json:"zrok_token"` + ZId string `json:"ziti_identity"` + ApiEndpoint string `json:"api_endpoint"` +}