go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/butlerlib/bootstrap/bootstrap.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 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 package bootstrap 16 17 import ( 18 "fmt" 19 20 "go.chromium.org/luci/common/errors" 21 "go.chromium.org/luci/common/system/environ" 22 "go.chromium.org/luci/config" 23 "go.chromium.org/luci/logdog/client/butlerlib/streamclient" 24 "go.chromium.org/luci/logdog/common/types" 25 "go.chromium.org/luci/logdog/common/viewer" 26 ) 27 28 // ErrNotBootstrapped is returned by Get when the current process is not 29 // bootstrapped. 30 var ErrNotBootstrapped = errors.New("not bootstrapped") 31 32 // Bootstrap contains information about the configured bootstrap environment. 33 // 34 // The bootstrap environment is loaded by probing the local application 35 // environment for variables emitted by a bootstrapping Butler. 36 type Bootstrap struct { 37 // CoordinatorHost is the name of the upstream Coordinator host. 38 // 39 // This is just the host name ("example.appspot.com"), not a full URL. 40 // 41 // If this instance is not configured using a production Coordinator Output, 42 // this will be empty. 43 CoordinatorHost string 44 45 // Project is the Butler instance project name. 46 Project string 47 // Prefix is the Butler instance prefix. 48 Prefix types.StreamName 49 // Namespace is prefix for stream names. 50 Namespace types.StreamName 51 52 // Client is the streamclient for this instance, or nil if the Butler has no 53 // streamserver. 54 Client *streamclient.Client 55 } 56 57 // GetFromEnv loads a Bootstrap instance from the given environment. 58 // 59 // It will return an error if the bootstrap data is invalid, and will return 60 // ErrNotBootstrapped if the current process is not bootstrapped. 61 func GetFromEnv(env environ.Env) (*Bootstrap, error) { 62 // Detect Butler by looking for EnvStreamServerPath in the envrironent. This 63 // is the only environment variable which matters for constructing a Butler 64 // Client; all the rest are just needed to assemble viewer URLs. 65 butlerSocket, ok := env.Lookup(EnvStreamServerPath) 66 if !ok { 67 return nil, ErrNotBootstrapped 68 } 69 70 bs := &Bootstrap{ 71 CoordinatorHost: env.Get(EnvCoordinatorHost), 72 Prefix: types.StreamName(env.Get(EnvStreamPrefix)), 73 Namespace: types.StreamName(env.Get(EnvNamespace)), 74 Project: env.Get(EnvStreamProject), 75 } 76 if err := bs.initializeClient(butlerSocket); err != nil { 77 return nil, fmt.Errorf("bootstrap: failed to create stream client [%s]: %s", butlerSocket, err) 78 } 79 80 if len(bs.Prefix) > 0 { 81 if err := bs.Prefix.Validate(); err != nil { 82 return nil, fmt.Errorf("bootstrap: failed to validate prefix %q: %s", bs.Prefix, err) 83 } 84 } 85 if len(bs.Project) > 0 { 86 if err := config.ValidateProjectName(bs.Project); err != nil { 87 return nil, fmt.Errorf("bootstrap: failed to validate project %q: %s", bs.Project, err) 88 } 89 } 90 if len(bs.Namespace) > 0 { 91 if err := bs.Namespace.Validate(); err != nil { 92 return nil, fmt.Errorf("bootstrap: failed to validate namespace %q: %s", bs.Namespace, err) 93 } 94 } 95 96 return bs, nil 97 } 98 99 func (bs *Bootstrap) initializeClient(v string) error { 100 c, err := streamclient.New(v, bs.Namespace) 101 if err != nil { 102 return errors.Annotate(err, "bootstrap: failed to create stream client [%s]", v).Err() 103 } 104 bs.Client = c 105 return nil 106 } 107 108 // Get is shorthand for `GetFromEnv(environ.System())`. 109 func Get() (*Bootstrap, error) { 110 return GetFromEnv(environ.System()) 111 } 112 113 // GetViewerURL returns a log stream viewer URL to the aggregate set of supplied 114 // stream paths. 115 // 116 // If both the Project and CoordinatorHost values are not populated, an error 117 // will be returned. 118 func (bs *Bootstrap) GetViewerURL(paths ...types.StreamPath) (string, error) { 119 if bs.Project == "" { 120 return "", errors.New("no project is configured") 121 } 122 if bs.CoordinatorHost == "" { 123 return "", errors.New("no coordinator host is configured") 124 } 125 return viewer.GetURL(bs.CoordinatorHost, bs.Project, paths...), nil 126 }