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 }