github.com/apptainer/singularity@v3.1.1+incompatible/pkg/build/types/definition.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package types 7 8 import ( 9 "bytes" 10 "encoding/json" 11 "fmt" 12 "io" 13 "strings" 14 ) 15 16 // Definition describes how to build an image. 17 type Definition struct { 18 Header map[string]string `json:"header"` 19 ImageData `json:"imageData"` 20 BuildData Data `json:"buildData"` 21 CustomData map[string]string `json:"customData"` 22 Raw []byte `json:"raw"` 23 } 24 25 // ImageData contains any scripts, metadata, etc... that needs to be 26 // present in some from in the final built image 27 type ImageData struct { 28 Metadata []byte `json:"metadata"` 29 Labels map[string]string `json:"labels"` 30 ImageScripts `json:"imageScripts"` 31 } 32 33 // ImageScripts contains scripts that are used after build time. 34 type ImageScripts struct { 35 Help string `json:"help"` 36 Environment string `json:"environment"` 37 Runscript string `json:"runScript"` 38 Test string `json:"test"` 39 Startscript string `json:"startScript"` 40 } 41 42 // Data contains any scripts, metadata, etc... that the Builder may 43 // need to know only at build time to build the image 44 type Data struct { 45 Files []FileTransport `json:"files"` 46 Scripts `json:"buildScripts"` 47 } 48 49 // FileTransport holds source and destination information of files to copy into the container 50 type FileTransport struct { 51 Src string `json:"source"` 52 Dst string `json:"destination"` 53 } 54 55 // Scripts defines scripts that are used at build time. 56 type Scripts struct { 57 Pre string `json:"pre"` 58 Setup string `json:"setup"` 59 Post string `json:"post"` 60 Test string `json:"test"` 61 } 62 63 // NewDefinitionFromURI crafts a new Definition given a URI 64 func NewDefinitionFromURI(uri string) (d Definition, err error) { 65 var u []string 66 if strings.Contains(uri, "://") { 67 u = strings.SplitN(uri, "://", 2) 68 } else if strings.Contains(uri, ":") { 69 u = strings.SplitN(uri, ":", 2) 70 } else { 71 return d, fmt.Errorf("build URI must start with prefix:// or prefix: ") 72 } 73 74 d = Definition{ 75 Header: map[string]string{ 76 "bootstrap": u[0], 77 "from": u[1], 78 }, 79 } 80 81 var buf bytes.Buffer 82 populateRaw(&d, &buf) 83 d.Raw = buf.Bytes() 84 85 return d, nil 86 } 87 88 // NewDefinitionFromJSON creates a new Definition using the supplied JSON. 89 func NewDefinitionFromJSON(r io.Reader) (d Definition, err error) { 90 decoder := json.NewDecoder(r) 91 92 for { 93 if err = decoder.Decode(&d); err == io.EOF { 94 break 95 } else if err != nil { 96 return 97 } 98 } 99 100 // if JSON definition doesn't have a raw data section, add it 101 if len(d.Raw) == 0 { 102 var buf bytes.Buffer 103 populateRaw(&d, &buf) 104 d.Raw = buf.Bytes() 105 } 106 107 return d, nil 108 } 109 110 func writeSectionIfExists(w io.Writer, ident string, s string) { 111 if len(s) > 0 { 112 w.Write([]byte("%")) 113 w.Write([]byte(ident)) 114 w.Write([]byte("\n")) 115 w.Write([]byte(s)) 116 w.Write([]byte("\n\n")) 117 } 118 } 119 120 func writeFilesIfExists(w io.Writer, f []FileTransport) { 121 122 if len(f) > 0 { 123 124 w.Write([]byte("%")) 125 w.Write([]byte("files")) 126 w.Write([]byte("\n")) 127 128 for _, ft := range f { 129 w.Write([]byte("\t")) 130 w.Write([]byte(ft.Src)) 131 w.Write([]byte("\t")) 132 w.Write([]byte(ft.Dst)) 133 w.Write([]byte("\n")) 134 } 135 w.Write([]byte("\n")) 136 } 137 } 138 139 func writeLabelsIfExists(w io.Writer, l map[string]string) { 140 141 if len(l) > 0 { 142 143 w.Write([]byte("%")) 144 w.Write([]byte("labels")) 145 w.Write([]byte("\n")) 146 147 for k, v := range l { 148 w.Write([]byte("\t")) 149 w.Write([]byte(k)) 150 w.Write([]byte(" ")) 151 w.Write([]byte(v)) 152 w.Write([]byte("\n")) 153 } 154 w.Write([]byte("\n")) 155 } 156 } 157 158 // populateRaw is a helper func to output a Definition struct 159 // into a definition file. 160 func populateRaw(d *Definition, w io.Writer) { 161 for k, v := range d.Header { 162 w.Write([]byte(k)) 163 w.Write([]byte(": ")) 164 w.Write([]byte(v)) 165 w.Write([]byte("\n")) 166 } 167 w.Write([]byte("\n")) 168 169 writeLabelsIfExists(w, d.ImageData.Labels) 170 writeFilesIfExists(w, d.BuildData.Files) 171 172 writeSectionIfExists(w, "help", d.ImageData.Help) 173 writeSectionIfExists(w, "environment", d.ImageData.Environment) 174 writeSectionIfExists(w, "runscript", d.ImageData.Runscript) 175 writeSectionIfExists(w, "test", d.ImageData.Test) 176 writeSectionIfExists(w, "startscript", d.ImageData.Startscript) 177 writeSectionIfExists(w, "pre", d.BuildData.Pre) 178 writeSectionIfExists(w, "setup", d.BuildData.Setup) 179 writeSectionIfExists(w, "post", d.BuildData.Post) 180 }