github.com/grailbio/base@v0.0.11/cmd/ticket-server/config/load.go (about)

     1  // Copyright 2018 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package config
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/grailbio/base/security/ticket"
    14  	"v.io/v23/naming"
    15  	"v.io/v23/security/access"
    16  	"v.io/x/lib/vlog"
    17  	"v.io/x/ref/lib/vdl/build"
    18  	"v.io/x/ref/lib/vdl/compile"
    19  	"v.io/x/ref/lib/vdl/vdlutil"
    20  )
    21  
    22  const configSuffix = ".vdlconfig"
    23  
    24  type ticketConfig struct {
    25  	Kind     string
    26  	Ticket   ticket.Ticket
    27  	Perms    access.Permissions
    28  	Controls map[ticket.Control]bool
    29  }
    30  
    31  type Config map[string]ticketConfig
    32  
    33  // Load returns a map with the tickets from a directory where the key is the
    34  // path of the ticket.
    35  func Load(dir string) (map[string]ticketConfig, error) {
    36  	all := Config{}
    37  
    38  	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
    39  		// The prefix we want for a path like 'config/docker/rwc.vdlconfig' is
    40  		// 'docker/rwc' where 'config' is the dir variable. For this example dirPart
    41  		// will be 'docker' and filePart will be 'rwc'.
    42  		dirPart := strings.TrimLeft(strings.TrimPrefix(filepath.Dir(path), dir), "/")
    43  		filePart := strings.TrimSuffix(filepath.Base(path), configSuffix)
    44  		prefix := naming.Join(dirPart, filePart)
    45  		vlog.VI(1).Infof("walk: %q prefix %q", path, prefix)
    46  
    47  		if !strings.HasSuffix(path, configSuffix) {
    48  			return nil
    49  		}
    50  
    51  		f, err := os.Open(path)
    52  		if err != nil {
    53  			return fmt.Errorf("bad path %q: %+v", path, err)
    54  		}
    55  
    56  		errors := vdlutil.Errors{}
    57  		warnings := vdlutil.Errors{}
    58  		packages := build.TransitivePackagesForConfig(path, f, build.Opts{}, &errors, &warnings)
    59  		env := compile.NewEnv(100)
    60  		for _, p := range packages {
    61  			vlog.VI(1).Infof("building package: %+v", p.Path)
    62  			build.BuildPackage(p, env)
    63  			if env.Errors.NumErrors() != 0 {
    64  				return fmt.Errorf("errors building %s:\n%+v", p.Path, env.Errors)
    65  			}
    66  		}
    67  
    68  		f.Seek(0, 0)
    69  		config := ticket.Config{}
    70  		build.BuildConfigValue(path, f, nil, env, &config)
    71  
    72  		if env.Errors != nil && env.Errors.NumErrors() > 0 {
    73  			return env.Errors.ToError()
    74  		}
    75  
    76  		for name, t := range config.Tickets {
    77  			perms := config.Permissions
    78  			if t.Permissions != nil {
    79  				perms = t.Permissions
    80  			}
    81  			// TODO(noah): Remove this check after PagerDutyId and TicketId controls are supported.
    82  			if _, ok := t.Controls[ticket.ControlRationale]; len(t.Controls) != 0 && (len(t.Controls) != 1 || !ok) {
    83  				return fmt.Errorf("only rationale control is supported: %+v", t.Controls)
    84  			}
    85  			all[naming.Join(prefix, name)] = ticketConfig{
    86  				Ticket:   t.Ticket,
    87  				Perms:    perms,
    88  				Controls: t.Controls,
    89  			}
    90  		}
    91  
    92  		return nil
    93  	})
    94  
    95  	return all, err
    96  }