github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/pkg/distribution/aciarchive.go (about)

     1  // Copyright 2016 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 distribution
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"path/filepath"
    21  
    22  	"github.com/PuerkitoBio/purell"
    23  	"github.com/appc/spec/schema"
    24  	"github.com/hashicorp/errwrap"
    25  )
    26  
    27  const (
    28  	distACIArchiveVersion = 0
    29  
    30  	// TypeACIArchive represents the ACIArchive distribution type
    31  	TypeACIArchive Type = "aci-archive"
    32  )
    33  
    34  func init() {
    35  	Register(TypeACIArchive, NewACIArchive)
    36  }
    37  
    38  // ACIArchive defines a distribution using an ACI file
    39  // The format is:
    40  // cimd:aci-archive:v=0:ArchiveURL?query...
    41  // The distribution type is "archive"
    42  // ArchiveURL must be query escaped
    43  // Examples:
    44  // cimd:aci-archive:v=0:file%3A%2F%2Fabsolute%2Fpath%2Fto%2Ffile
    45  // cimd:aci-archive:v=0:https%3A%2F%2Fexample.com%2Fapp.aci
    46  type ACIArchive struct {
    47  	cimdURL      *url.URL // the cimd URL as explained in the examples
    48  	transportURL *url.URL // the transport URL of the target ACI archive, i.e. https://example.com/app.aci
    49  
    50  	str string // the string representation
    51  }
    52  
    53  // NewACIArchive creates a new aci-archive distribution from the provided distribution uri.
    54  func NewACIArchive(u *url.URL) (Distribution, error) {
    55  	c, err := parseCIMD(u)
    56  	if err != nil {
    57  		return nil, fmt.Errorf("cannot parse URI: %q: %v", u.String(), err)
    58  	}
    59  	if c.Type != TypeACIArchive {
    60  		return nil, fmt.Errorf("illegal ACI archive distribution type: %q", c.Type)
    61  	}
    62  
    63  	// This should be a valid URL
    64  	data, err := url.QueryUnescape(c.Data)
    65  	if err != nil {
    66  		return nil, errwrap.Wrap(fmt.Errorf("error unescaping url %q", c.Data), err)
    67  	}
    68  	aciu, err := url.Parse(data)
    69  	if err != nil {
    70  		return nil, errwrap.Wrap(fmt.Errorf("error parsing url %q", c.Data), err)
    71  	}
    72  
    73  	// save the URI as sorted to make it ready for comparison
    74  	purell.NormalizeURL(u, purell.FlagSortQuery)
    75  
    76  	str := u.String()
    77  	if path := aciu.String(); filepath.Ext(path) == schema.ACIExtension {
    78  		str = path
    79  	}
    80  
    81  	return &ACIArchive{
    82  		cimdURL:      u,
    83  		transportURL: aciu,
    84  		str:          str,
    85  	}, nil
    86  }
    87  
    88  // NewACIArchiveFromTransportURL creates a new aci-archive distribution from the provided transport URL
    89  // Example: file:///full/path/to/aci/file.aci -> cimd:aci-archive:v=0:file%3A%2F%2F%2Ffull%2Fpath%2Fto%2Faci%2Ffile.aci
    90  func NewACIArchiveFromTransportURL(u *url.URL) (Distribution, error) {
    91  	urlStr := NewCIMDString(TypeACIArchive, distACIArchiveVersion, url.QueryEscape(u.String()))
    92  	u, err := url.Parse(urlStr)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	return NewACIArchive(u)
    97  }
    98  
    99  func (a *ACIArchive) CIMD() *url.URL {
   100  	// Create a copy of the URL.
   101  	u, err := url.Parse(a.cimdURL.String())
   102  	if err != nil {
   103  		panic(err)
   104  	}
   105  	return u
   106  }
   107  
   108  func (a *ACIArchive) Equals(d Distribution) bool {
   109  	a2, ok := d.(*ACIArchive)
   110  	if !ok {
   111  		return false
   112  	}
   113  
   114  	return a.CIMD().String() == a2.CIMD().String()
   115  }
   116  
   117  func (a *ACIArchive) String() string {
   118  	return a.str
   119  }
   120  
   121  // TransportURL returns a copy of the transport URL.
   122  func (a *ACIArchive) TransportURL() *url.URL {
   123  	// Create a copy of the transport URL
   124  	tu, err := url.Parse(a.transportURL.String())
   125  	if err != nil {
   126  		panic(fmt.Errorf("invalid transport URL: %v", err))
   127  	}
   128  
   129  	return tu
   130  }