github.com/emcfarlane/larking@v0.0.0-20220605172417-1704b45ee6c3/starlib/starlarkrule/container.go (about) 1 package starlarkrule 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/containerd/stargz-snapshotter/estargz" 8 "github.com/emcfarlane/larking/starlib/starext" 9 "github.com/emcfarlane/larking/starlib/starlarkstruct" 10 "github.com/emcfarlane/larking/starlib/starlarkthread" 11 "github.com/google/go-containerregistry/pkg/authn" 12 cname "github.com/google/go-containerregistry/pkg/name" 13 v1 "github.com/google/go-containerregistry/pkg/v1" 14 "github.com/google/go-containerregistry/pkg/v1/empty" 15 "github.com/google/go-containerregistry/pkg/v1/mutate" 16 "github.com/google/go-containerregistry/pkg/v1/remote" 17 "github.com/google/go-containerregistry/pkg/v1/tarball" 18 "go.starlark.net/starlark" 19 ) 20 21 // conatiner rules implemented with go-containerregistry. 22 // Based on: 23 // https://github.com/google/ko/blob/main/pkg/build/gobuild.go 24 // https://github.com/bazelbuild/rules_docker/tree/master/container 25 var containerModule = &starlarkstruct.Module{ 26 Name: "container", 27 Members: starlark.StringDict{ 28 "pull": starext.MakeBuiltin("container.pull", containerPull), 29 "build": starext.MakeBuiltin("container.build", containerBuild), 30 "push": starext.MakeBuiltin("container.push", containerPush), 31 }, 32 } 33 34 const ImageConstructor starlark.String = "container.image" 35 36 // TODO: return starlark provider. 37 func NewContainerImage(filename, reference string) starlark.Value { 38 return starlarkstruct.FromStringDict(ImageConstructor, map[string]starlark.Value{ 39 "name": starlark.String(filename), 40 "reference": starlark.String(reference), 41 }) 42 } 43 44 func containerPull(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 45 var ( 46 name string 47 reference string 48 ) 49 if err := starlark.UnpackArgs( 50 fnname, args, kwargs, 51 "name", &name, "reference", &reference, 52 ); err != nil { 53 return nil, err 54 } 55 56 ref, err := cname.ParseReference(reference) 57 if err != nil { 58 return nil, err 59 } 60 ref.Context() 61 62 ctx := starlarkthread.GetContext(thread) 63 64 fmt.Println("image?") 65 img, err := remote.Image(ref, 66 remote.WithAuthFromKeychain(authn.DefaultKeychain), 67 remote.WithContext(ctx), 68 ) 69 if err != nil { 70 fmt.Println("image?", err) 71 return nil, err 72 } 73 74 l, err := ParseRelativeLabel(thread.Name, name) 75 if err != nil { 76 return nil, err 77 } 78 fmt.Println("HERE?", l) 79 80 blobs := starext.Blobs{} 81 defer blobs.Close() 82 83 // TODO: caching. 84 // HACK: check we have hash. 85 var rebuild = true 86 87 if rebuild { 88 w, err := blobs.NewWriter(ctx, l.BucketURL(), l.Key(), nil) 89 if err != nil { 90 return nil, err 91 } 92 defer w.Close() 93 94 if err := tarball.Write(ref, img, w); err != nil { 95 return nil, err 96 } 97 } 98 fmt.Println("HERE??", l) 99 return l, nil 100 } 101 102 func listToStrings(l *starlark.List) ([]string, error) { 103 iter := l.Iterate() 104 defer iter.Done() 105 106 var ss []string 107 var x starlark.Value 108 for iter.Next(&x) { 109 s, ok := starlark.AsString(x) 110 if !ok { 111 return nil, fmt.Errorf("invalid string list") 112 } 113 ss = append(ss, s) 114 } 115 return ss, nil 116 } 117 118 func containerInfo(args *AttrArgs) (src *Label, ref string, err error) { 119 srcValue, err := args.Attr("src") 120 if err != nil { 121 return nil, "", err 122 } 123 src, ok := srcValue.(*Label) 124 if !ok { 125 return nil, "", fmt.Errorf("invalid source") 126 } 127 128 refValue, err := args.Attr("reference") 129 if err != nil { 130 return nil, "", err 131 } 132 ref, ok = starlark.AsString(refValue) 133 if !ok { 134 return nil, "", fmt.Errorf("invalid reference") 135 } 136 return src, ref, nil 137 } 138 139 func containerBuild(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 140 ctx := starlarkthread.GetContext(thread) 141 142 var ( 143 name string 144 entrypointList *starlark.List 145 tar *Label 146 base *AttrArgs //*Label 147 prioritizedList *starlark.List 148 reference string 149 ) 150 if err := starlark.UnpackArgs( 151 fnname, args, kwargs, 152 "name", &name, 153 "entrypoint", &entrypointList, 154 "tar", &tar, 155 "reference", &reference, 156 "base?", &base, 157 "prioritized_files?", &prioritizedList, 158 ); err != nil { 159 return nil, err 160 } 161 162 // TODO: load tag from provider? 163 entrypoint, err := listToStrings(entrypointList) 164 if err != nil { 165 return nil, err 166 } 167 prioritizedFiles, err := listToStrings(prioritizedList) 168 if err != nil { 169 return nil, err 170 } 171 172 blobs := starext.Blobs{} 173 defer blobs.Close() 174 175 baseImage := empty.Image 176 if base != nil { 177 src, reference, err := containerInfo(base) 178 if err != nil { 179 return nil, err 180 } 181 182 //// Load base image from local. 183 //imageProvider, err := toStruct(base, ImageConstructor) 184 //if err != nil { 185 // return nil, fmt.Errorf("image provider: %w", err) 186 //} 187 //if err := assertConstructor(imageProvider, ImageConstructor); err != nil { 188 // return nil, err 189 //} 190 191 //filename, err := getAttrStr(imageProvider, "name") 192 //if err != nil { 193 // return nil, err 194 //} 195 196 //reference, err := getAttrStr(imageProvider, "reference") 197 //if err != nil { 198 // return nil, err 199 //} 200 201 tag, err := cname.NewTag(reference, cname.StrictValidation) 202 if err != nil { 203 return nil, err 204 } 205 206 opener := func() (io.ReadCloser, error) { 207 return blobs.NewReader(ctx, src.BucketURL(), src.Key(), nil) 208 } 209 210 // Load base from filesystem. 211 img, err := tarball.Image(opener, &tag) 212 if err != nil { 213 return nil, err 214 } 215 baseImage = img 216 } 217 218 var layers []mutate.Addendum 219 220 //tarStruct, err := toStruct(tar, FileConstructor) 221 //if err != nil { 222 // return nil, err 223 //} 224 //tarFilename, err := getAttrStr(tarStruct, "path") 225 //if err != nil { 226 // return nil, err 227 //} 228 229 r, err := blobs.NewReader(ctx, tar.BucketURL(), tar.Key(), nil) 230 if err != nil { 231 return nil, err 232 } 233 defer r.Close() 234 235 imageLayer, err := tarball.LayerFromReader( 236 r, tarball.WithCompressedCaching, 237 tarball.WithEstargzOptions(estargz.WithPrioritizedFiles( 238 // When using estargz, prioritize downloading the binary entrypoint. 239 prioritizedFiles, 240 )), 241 ) 242 if err != nil { 243 return nil, err 244 } 245 layers = append(layers, mutate.Addendum{ 246 Layer: imageLayer, 247 History: v1.History{ 248 Author: "laze", 249 CreatedBy: "laze " + name, 250 Comment: "ship it real good", 251 }, 252 }) 253 254 // Augment the base image with our application layer. 255 appImage, err := mutate.Append(baseImage, layers...) 256 if err != nil { 257 return nil, err 258 } 259 260 // Start from a copy of the base image's config file, and set 261 // the entrypoint to our app. 262 cfg, err := appImage.ConfigFile() 263 if err != nil { 264 return nil, err 265 } 266 cfg = cfg.DeepCopy() 267 cfg.Config.Entrypoint = entrypoint 268 //updatePath(cfg) 269 cfg.Config.Env = append(cfg.Config.Env, "LAZE_DATA_PATH="+"/") // TODO 270 cfg.Author = "github.com/emcfarlane/laze" 271 272 if cfg.Config.Labels == nil { 273 cfg.Config.Labels = map[string]string{} 274 } 275 // TODO: Add support for labels. 276 //for k, v := range labels { 277 // cfg.Config.Labels[k] = v 278 //} 279 280 img, err := mutate.ConfigFile(appImage, cfg) 281 if err != nil { 282 return nil, err 283 } 284 285 //empty := v1.Time{} 286 //if g.creationTime != empty { 287 // return mutate.CreatedAt(image, g.creationTime) 288 //} 289 290 l, err := ParseRelativeLabel(thread.Name, name) 291 if err != nil { 292 return nil, err 293 } 294 295 w, err := blobs.NewWriter(ctx, l.BucketURL(), l.Key(), nil) 296 if err != nil { 297 return nil, err 298 } 299 defer w.Close() 300 301 //filename := "" // c.key 302 //f, err := os.Create(filename) 303 //if err != nil { 304 // panic(err) 305 //} 306 //defer f.Close() 307 308 ref, err := cname.ParseReference(reference) 309 if err != nil { 310 return nil, err 311 } 312 313 if err := tarball.Write(ref, img, w); err != nil { 314 return nil, err 315 } 316 return l, nil 317 //return NewContainerImage(filename, reference), nil 318 } 319 320 func containerPush(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 321 fmt.Println("RUNNING CONTAINER PUSH") 322 ctx := starlarkthread.GetContext(thread) 323 var ( 324 name string 325 image *AttrArgs 326 reference string 327 ) 328 if err := starlark.UnpackArgs( 329 fnname, args, kwargs, 330 "name", &name, 331 "image", &image, 332 "reference", &reference, 333 ); err != nil { 334 fmt.Println("failed on starlark") 335 return nil, err 336 } 337 338 src, reference, err := containerInfo(image) 339 if err != nil { 340 return nil, err 341 } 342 343 //imageProvider, err := toStruct(image, ImageConstructor) 344 //if err != nil { 345 // return nil, fmt.Errorf("image provider: %w", err) 346 //} 347 348 //// TODO: should it be a file provider? 349 //filename, err := getAttrStr(imageProvider, "name") 350 //if err != nil { 351 // return nil, err 352 //} 353 //imageRef, err := getAttrStr(imageProvider, "reference") 354 //if err != nil { 355 // return nil, err 356 //} 357 358 tag, err := cname.NewTag(reference, cname.StrictValidation) 359 if err != nil { 360 fmt.Println("FAILED ON tag") 361 return nil, err 362 } 363 364 blobs := starext.Blobs{} 365 defer blobs.Close() 366 367 opener := func() (io.ReadCloser, error) { 368 return blobs.NewReader(ctx, src.BucketURL(), src.Key(), nil) 369 } 370 371 // Load base from filesystem. 372 img, err := tarball.Image(opener, &tag) 373 if err != nil { 374 fmt.Println("FAILED ON image load") 375 return nil, err 376 } 377 378 ref, err := cname.ParseReference(reference) 379 if err != nil { 380 fmt.Println("FAILED ON REF") 381 return nil, err 382 } 383 384 if err := remote.Write(ref, img, 385 remote.WithAuthFromKeychain(authn.DefaultKeychain), 386 remote.WithContext(ctx), 387 ); err != nil { 388 fmt.Println("failing here?", err) 389 return nil, err 390 } 391 return src, nil 392 //return NewContainerImage(filename, reference), nil 393 }