github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/contextconfig.go (about)

     1  /*
     2   * Copyright 2018 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package cmd
    17  
    18  import (
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"sort"
    23  
    24  	"github.com/nats-io/nsc/v2/cmd/store"
    25  )
    26  
    27  type ContextConfig struct {
    28  	StoreRoot string `json:"store_root"` // where the projects are
    29  	Operator  string `json:"operator"`
    30  	Account   string `json:"account"`
    31  }
    32  
    33  func NewContextConfig(storeRoot string) (*ContextConfig, error) {
    34  	ctx := ContextConfig{}
    35  	dc := GetCwdCtx()
    36  	if dc != nil {
    37  		ctx = *dc
    38  		config.SetDefaults()
    39  		ctx.setStoreRoot(ctx.StoreRoot)
    40  		return &ctx, nil
    41  	}
    42  
    43  	if err := ctx.setStoreRoot(storeRoot); err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	return &ctx, nil
    48  }
    49  
    50  func (c *ContextConfig) setStoreRoot(storeRoot string) error {
    51  	var err error
    52  	if storeRoot != "" {
    53  		c.StoreRoot, err = filepath.Abs(storeRoot)
    54  		if err != nil {
    55  			return err
    56  		}
    57  		c.SetDefaults()
    58  	}
    59  	return nil
    60  }
    61  
    62  func (c *ContextConfig) Update(root string, operator string, account string) error {
    63  	if err := c.setStoreRoot(root); err != nil {
    64  		return err
    65  	}
    66  	if operator != "" {
    67  		if err := c.SetOperator(operator); err != nil {
    68  			return err
    69  		}
    70  	}
    71  	if account != "" {
    72  		if err := c.SetAccount(account); err != nil {
    73  			return err
    74  		}
    75  	}
    76  	c.SetDefaults()
    77  	return nil
    78  }
    79  
    80  // deduce as much context as possible
    81  func (c *ContextConfig) SetDefaults() {
    82  	if err := IsValidDir(c.StoreRoot); err == nil {
    83  		operators := c.ListOperators()
    84  		c.Operator = c.saneDefault(c.Operator, operators)
    85  		if c.Operator == "" {
    86  			// reset everything
    87  			c.Account = ""
    88  			return
    89  		}
    90  
    91  		s, err := c.LoadStore(c.Operator)
    92  		if err != nil {
    93  			return
    94  		}
    95  		accounts, err := s.ListSubContainers(store.Accounts)
    96  		if err == nil {
    97  			c.Account = c.saneDefault(c.Account, accounts)
    98  		}
    99  	}
   100  }
   101  
   102  // saneDefault keeps the current choice if it exists, or returns ""
   103  func (c *ContextConfig) saneDefault(current string, choices []string) string {
   104  	switch len(choices) {
   105  	case 0:
   106  		return ""
   107  	case 1:
   108  		return choices[0]
   109  	default:
   110  		for _, v := range choices {
   111  			if v == current {
   112  				return current
   113  			}
   114  		}
   115  		return ""
   116  	}
   117  }
   118  
   119  func (c *ContextConfig) LoadStore(operatorName string) (*store.Store, error) {
   120  	return store.LoadStore(filepath.Join(c.StoreRoot, operatorName))
   121  }
   122  
   123  func (c *ContextConfig) ListOperators() []string {
   124  	dirEntries, err := os.ReadDir(c.StoreRoot)
   125  	if err != nil {
   126  		return nil
   127  	}
   128  	var operators []string
   129  	for _, v := range dirEntries {
   130  		name := store.SafeName(filepath.Base(v.Name()))
   131  		fp := filepath.Join(c.StoreRoot, name, store.NSCFile)
   132  		info, err := os.Stat(fp)
   133  		if err == nil && info != nil {
   134  			operators = append(operators, v.Name())
   135  		}
   136  	}
   137  	sort.Strings(operators)
   138  	return operators
   139  }
   140  
   141  func (c *ContextConfig) SetOperator(operator string) error {
   142  	ok := true
   143  	if operator != "" {
   144  		ok = false
   145  		for _, v := range c.ListOperators() {
   146  			if v == operator {
   147  				ok = true
   148  				break
   149  			}
   150  		}
   151  	}
   152  	if !ok {
   153  		return fmt.Errorf("operator %q not in %#q", operator, c.StoreRoot)
   154  	}
   155  
   156  	c.Operator = operator
   157  	return GetConfig().Save()
   158  }
   159  
   160  func (c *ContextConfig) SetAccount(account string) error {
   161  	if err := c.SetAccountTemp(account); err != nil {
   162  		return err
   163  	}
   164  	return GetConfig().Save()
   165  }
   166  
   167  func (c *ContextConfig) SetAccountTemp(account string) error {
   168  	if account != "" {
   169  		if err := c.hasSubContainer(store.Accounts, account); err != nil {
   170  			return err
   171  		}
   172  	}
   173  	c.Account = account
   174  
   175  	return nil
   176  }
   177  
   178  func (c *ContextConfig) ListAccounts() ([]string, error) {
   179  	return c.getSubContainers(store.Accounts)
   180  }
   181  
   182  func (c *ContextConfig) getSubContainers(kind string) ([]string, error) {
   183  	s, err := store.LoadStore(filepath.Join(c.StoreRoot, c.Operator))
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	names, err := s.ListSubContainers(kind)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	return names, nil
   192  }
   193  
   194  func (c *ContextConfig) hasSubContainer(kind string, name string) error {
   195  	names, err := c.getSubContainers(kind)
   196  	if err != nil {
   197  		return err
   198  	}
   199  	for _, v := range names {
   200  		if name == v {
   201  			return nil
   202  		}
   203  	}
   204  	return fmt.Errorf("%q not in %s for operator %q in %#q", name, kind, c.Operator, c.StoreRoot)
   205  }