github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/stage0/registration.go (about) 1 // Copyright 2014 The rkt 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 stage0 16 17 import ( 18 "crypto/rand" 19 "errors" 20 "fmt" 21 "io" 22 "net" 23 "net/http" 24 "net/url" 25 "os" 26 "path" 27 "path/filepath" 28 "syscall" 29 "time" 30 31 "github.com/appc/spec/schema" 32 "github.com/appc/spec/schema/types" 33 "github.com/hashicorp/errwrap" 34 35 "github.com/rkt/rkt/common" 36 ) 37 38 const ( 39 retryCount = 3 40 41 mdsRegisteredFile = "./mds-registered" 42 ) 43 44 var retryPause = time.Second 45 46 var errUnreachable = errors.New(`could not reach the metadata service. 47 Make sure metadata service is currently running or use 48 "rkt run --mds-register=false" to skip pod registration. 49 For more information on running metadata service, 50 see https://github.com/rkt/rkt/blob/master/Documentation/subcommands/metadata-service.md`) 51 52 // registerPod registers pod with metadata service. 53 // Returns authentication token to be passed in the URL 54 func registerPod(root string, uuid *types.UUID, apps schema.AppList) (token string, rerr error) { 55 u := uuid.String() 56 57 var err error 58 token, err = generateMDSToken() 59 if err != nil { 60 rerr = errwrap.Wrap(errors.New("failed to generate MDS token"), err) 61 return 62 } 63 64 pmfPath := common.PodManifestPath(root) 65 pmf, err := os.Open(pmfPath) 66 if err != nil { 67 rerr = errwrap.Wrap(fmt.Errorf("failed to open runtime manifest (%v)", pmfPath), err) 68 return 69 } 70 71 pth := fmt.Sprintf("/pods/%v?token=%v", u, token) 72 err = httpRequest("PUT", pth, pmf) 73 pmf.Close() 74 if err != nil { 75 rerr = errwrap.Wrap(errors.New("failed to register pod with metadata svc"), err) 76 return 77 } 78 79 defer func() { 80 if rerr != nil { 81 unregisterPod(root, uuid) 82 } 83 }() 84 85 rf, err := os.Create(filepath.Join(root, mdsRegisteredFile)) 86 if err != nil { 87 rerr = errwrap.Wrap(errors.New("failed to create mds-register file"), err) 88 return 89 } 90 rf.Close() 91 92 for _, app := range apps { 93 ampath := common.ImageManifestPath(root, app.Name) 94 amf, err := os.Open(ampath) 95 if err != nil { 96 rerr = errwrap.Wrap(fmt.Errorf("failed reading app manifest %q", ampath), err) 97 return 98 } 99 100 err = registerApp(u, app.Name.String(), amf) 101 amf.Close() 102 if err != nil { 103 rerr = errwrap.Wrap(errors.New("failed to register app with metadata svc"), err) 104 return 105 } 106 } 107 108 return 109 } 110 111 // unregisterPod unregisters pod with the metadata service. 112 func unregisterPod(root string, uuid *types.UUID) error { 113 _, err := os.Stat(filepath.Join(root, mdsRegisteredFile)) 114 switch { 115 case err == nil: 116 pth := path.Join("/pods", uuid.String()) 117 return httpRequest("DELETE", pth, nil) 118 119 case os.IsNotExist(err): 120 return nil 121 122 default: 123 return err 124 } 125 } 126 127 func generateMDSToken() (string, error) { 128 bytes := make([]byte, 16) 129 _, err := rand.Read(bytes) 130 if err != nil { 131 return "", err 132 } 133 return fmt.Sprintf("%x", bytes), nil 134 } 135 136 func registerApp(uuid, app string, r io.Reader) error { 137 pth := path.Join("/pods", uuid, app) 138 return httpRequest("PUT", pth, r) 139 } 140 141 func httpRequest(method, pth string, body io.Reader) error { 142 uri := "http://unixsock" + pth 143 144 t := &http.Transport{ 145 Dial: func(_, _ string) (net.Conn, error) { 146 return net.Dial("unix", common.MetadataServiceRegSock) 147 }, 148 } 149 150 var err error 151 for i := 0; i < retryCount; i++ { 152 var req *http.Request 153 req, err = http.NewRequest(method, uri, body) 154 if err != nil { 155 return err 156 } 157 158 cli := http.Client{Transport: t} 159 160 var resp *http.Response 161 resp, err = cli.Do(req) 162 switch { 163 case err == nil: 164 defer resp.Body.Close() 165 166 if resp.StatusCode != 200 { 167 return fmt.Errorf("%v %v returned %v", method, pth, resp.StatusCode) 168 } 169 170 return nil 171 172 default: 173 log.Error(err) 174 time.Sleep(retryPause) 175 } 176 } 177 178 if urlErr, ok := err.(*url.Error); ok { 179 if opErr, ok := urlErr.Err.(*net.OpError); ok { 180 errno := opErr.Err 181 // in go1.5 syscall errors in OpError.Err are of type 182 // os.SyscallError instead of directly syscall.Errno 183 if sysErr, ok := opErr.Err.(*os.SyscallError); ok { 184 errno = sysErr.Err 185 } 186 if errno == syscall.ENOENT || errno == syscall.ENOTSOCK { 187 return errUnreachable 188 } 189 } 190 } 191 192 return err 193 } 194 195 // CheckMdsAvailability checks whether a local metadata service can be reached. 196 func CheckMdsAvailability() error { 197 if conn, err := net.Dial("unix", common.MetadataServiceRegSock); err != nil { 198 return errUnreachable 199 } else { 200 conn.Close() 201 return nil 202 } 203 }