blob: 78e5b599ce9f5fbbb0ee528c8ad13d358603a734 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package ffxutil
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/xeipuuv/gojsonpointer"
"go.fuchsia.dev/fuchsia/tools/lib/jsonutil"
)
// FFXConfig describes a config to run ffx with.
type FFXConfig struct {
socket string
config map[string]interface{}
env []string
}
// newIsolatedFFXConfig creates a config that provides an isolated environment to run ffx in.
func newIsolatedFFXConfig(dir string) *FFXConfig {
socketPath := filepath.Join(dir, "ffx_socket")
config := &FFXConfig{
socket: socketPath,
config: make(map[string]interface{}),
env: buildConfigEnv(socketPath),
}
config.SetJsonPointer("/overnet/socket", socketPath)
config.SetJsonPointer("/log/dir", []string{filepath.Join(dir, "logs")})
config.SetJsonPointer("/test/output_path", []string{filepath.Join(dir, "saved_test_runs")})
config.SetJsonPointer("/fastboot/usb/disabled", true)
config.SetJsonPointer("/ffx/analytics/disabled", true)
return config
}
func buildConfigEnv(socketPath string) []string {
// TODO(fxbug.dev/64499): Stop setting environment variables once bug is fixed.
// The env vars to set come from //src/developer/ffx/plugins/self-test/src/test/mod.rs.
// TEMP, TMP, HOME, and XDG_CONFIG_HOME are set in //tools/lib/environment/environment.go
// with a call to environment.Ensure().
envMap := map[string]string{
"ASCENDD": socketPath,
}
var env []string
for k, v := range envMap {
env = append(env, fmt.Sprintf("%s=%s", k, v))
}
return env
}
// Env returns the environment that should be set when running ffx with this config.
func (c *FFXConfig) Env() []string {
return c.env
}
// Set sets values in the config.
func (c *FFXConfig) Set(key string, value interface{}) {
c.config[key] = value
}
// SetJsonPointer sets the config value at the RFC 6901 JSON Pointer provided.
func (c *FFXConfig) SetJsonPointer(pointer string, value interface{}) {
if p, err := gojsonpointer.NewJsonPointer(pointer); err == nil {
if _, err := p.Set(c.config, value); err == nil {
return
}
}
// The config field might not have been set yet, so go
// through the keys in the pointer and set the values
// where missing.
config := c.config
keys := strings.Split(strings.Trim(pointer, "/"), "/")
for i, key := range keys {
if i == len(keys)-1 {
config[key] = value
break
}
if _, ok := config[key]; !ok {
config[key] = make(map[string]interface{})
}
config = config[key].(map[string]interface{})
}
}
// GetJsonPointer gets the config value at the RFC 6901 JSON Pointer provided.
func (c *FFXConfig) GetJsonPointer(pointer string) interface{} {
p, err := gojsonpointer.NewJsonPointer(pointer)
if err != nil {
return nil
}
value, _, err := p.Get(c.config)
if err != nil {
return nil
}
return value
}
// ToFile writes the config to a file.
func (c *FFXConfig) ToFile(configPath string) error {
if err := os.MkdirAll(filepath.Dir(configPath), os.ModePerm); err != nil {
return err
}
return jsonutil.WriteToFile(configPath, c.config)
}
func configFromFile(configPath string) (*FFXConfig, error) {
var config map[string]interface{}
if err := jsonutil.ReadFromFile(configPath, &config); err != nil {
return nil, fmt.Errorf("jsonutil.ReadFromFile(%q, _) = %w", configPath, err)
}
p, err := gojsonpointer.NewJsonPointer("/overnet/socket")
if err != nil {
return nil, err
}
socketPathVal, _, err := p.Get(config)
if err != nil {
return nil, fmt.Errorf("error getting overnet.socket from config %q: %w", configPath, err)
}
socketPath, ok := socketPathVal.(string)
if !ok {
return nil, fmt.Errorf("overnet.socket at config %q is not a string", configPath)
}
return &FFXConfig{
socket: socketPath,
config: config,
env: buildConfigEnv(socketPath),
}, nil
}
// Close removes the socket if it hasn't been removed by `ffx daemon stop`.
func (c *FFXConfig) Close() error {
if c.socket == "" {
return nil
}
if _, err := os.Stat(c.socket); !os.IsNotExist(err) {
return os.Remove(c.socket)
}
return nil
}