github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/pkg/aci/aci.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 aci implements helper functions for working with ACIs 16 package aci 17 18 import ( 19 "archive/tar" 20 "bytes" 21 "encoding/json" 22 "errors" 23 "io" 24 "io/ioutil" 25 "os" 26 "strings" 27 "time" 28 29 "github.com/appc/spec/aci" 30 "github.com/appc/spec/schema" 31 "github.com/appc/spec/schema/types" 32 "github.com/hashicorp/errwrap" 33 "golang.org/x/crypto/openpgp" 34 ) 35 36 type ACIEntry struct { 37 Header *tar.Header 38 Contents string 39 } 40 41 type imageArchiveWriter struct { 42 *tar.Writer 43 am *schema.ImageManifest 44 } 45 46 // NewImageWriter creates a new ArchiveWriter which will generate an App 47 // Container Image based on the given manifest and write it to the given 48 // tar.Writer 49 // TODO(sgotti) this is a copy of appc/spec/aci.imageArchiveWriter with 50 // addFileNow changed to create the file with the current user. needed for 51 // testing as non root user. 52 func NewImageWriter(am schema.ImageManifest, w *tar.Writer) aci.ArchiveWriter { 53 aw := &imageArchiveWriter{ 54 w, 55 &am, 56 } 57 return aw 58 } 59 60 func (aw *imageArchiveWriter) AddFile(hdr *tar.Header, r io.Reader) error { 61 err := aw.Writer.WriteHeader(hdr) 62 if err != nil { 63 return err 64 } 65 66 if r != nil { 67 _, err := io.Copy(aw.Writer, r) 68 if err != nil { 69 return err 70 } 71 } 72 73 return nil 74 } 75 76 func (aw *imageArchiveWriter) addFileNow(path string, contents []byte) error { 77 buf := bytes.NewBuffer(contents) 78 now := time.Now() 79 hdr := tar.Header{ 80 Name: path, 81 Mode: 0644, 82 Uid: os.Getuid(), 83 Gid: os.Getgid(), 84 Size: int64(buf.Len()), 85 ModTime: now, 86 Typeflag: tar.TypeReg, 87 ChangeTime: now, 88 } 89 return aw.AddFile(&hdr, buf) 90 } 91 92 func (aw *imageArchiveWriter) addManifest(name string, m json.Marshaler) error { 93 out, err := m.MarshalJSON() 94 if err != nil { 95 return err 96 } 97 return aw.addFileNow(name, out) 98 } 99 100 func (aw *imageArchiveWriter) Close() error { 101 if err := aw.addManifest(aci.ManifestFile, aw.am); err != nil { 102 return err 103 } 104 return aw.Writer.Close() 105 } 106 107 // NewBasicACI creates a new ACI in the given directory with the given name. 108 // Used for testing. 109 func NewBasicACI(dir string, name string) (*os.File, error) { 110 manifest := schema.ImageManifest{ 111 ACKind: schema.ImageManifestKind, 112 ACVersion: schema.AppContainerVersion, 113 Name: types.ACIdentifier(name), 114 } 115 116 b, err := manifest.MarshalJSON() 117 if err != nil { 118 return nil, err 119 } 120 121 return NewACI(dir, string(b), nil) 122 } 123 124 // NewACI creates a new ACI in the given directory with the given image 125 // manifest and entries. 126 // Used for testing. 127 func NewACI(dir string, manifest string, entries []*ACIEntry) (*os.File, error) { 128 var im schema.ImageManifest 129 if err := im.UnmarshalJSON([]byte(manifest)); err != nil { 130 return nil, errwrap.Wrap(errors.New("invalid image manifest"), err) 131 } 132 133 tf, err := ioutil.TempFile(dir, "") 134 if err != nil { 135 return nil, err 136 } 137 defer os.Remove(tf.Name()) 138 139 tw := tar.NewWriter(tf) 140 aw := NewImageWriter(im, tw) 141 142 for _, entry := range entries { 143 // Add default mode 144 if entry.Header.Mode == 0 { 145 if entry.Header.Typeflag == tar.TypeDir { 146 entry.Header.Mode = 0755 147 } else { 148 entry.Header.Mode = 0644 149 } 150 } 151 // Add calling user uid and gid or tests will fail 152 entry.Header.Uid = os.Getuid() 153 entry.Header.Gid = os.Getgid() 154 sr := strings.NewReader(entry.Contents) 155 if err := aw.AddFile(entry.Header, sr); err != nil { 156 return nil, err 157 } 158 } 159 160 if err := aw.Close(); err != nil { 161 return nil, err 162 } 163 return tf, nil 164 } 165 166 // NewDetachedSignature creates a new openpgp armored detached signature for the given ACI 167 // signed with armoredPrivateKey. 168 func NewDetachedSignature(armoredPrivateKey string, aci io.Reader) (io.Reader, error) { 169 entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(armoredPrivateKey)) 170 if err != nil { 171 return nil, err 172 } 173 if len(entityList) < 1 { 174 return nil, errors.New("empty entity list") 175 } 176 signature := &bytes.Buffer{} 177 if err := openpgp.ArmoredDetachSign(signature, entityList[0], aci, nil); err != nil { 178 return nil, err 179 } 180 return signature, nil 181 }