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  }