Skip to content
This repository has been archived by the owner on Aug 14, 2020. It is now read-only.

Add assets loading, binary selection and VCS information in manifest #13

Merged
merged 21 commits into from
Mar 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@

## Usage

Use goaci as you would `go get`:
Use `goaci` as you would `go get`:

$ goaci github.com/coreos/etcd
Wrote etcd.aci
$ actool -debug validate etcd.aci
etcd.aci: valid app container image

`goaci` provides options for specifying assets, adding arguments for an application, selecting binary is going to be packaged in final ACI and so on. Use --help to read about them.

## How it works

`goaci` creates a temporary directory and uses it as a `GOPATH`; it then `go get`s the specified package and compiles it statically.
Then it generates a very basic image manifest (using mostly default values, configurables coming soon) and leverages the [appc/spec](https://github.com/appc/spec) libraries to construct an ACI.
`goaci` creates a temporary directory and uses it as a `GOPATH` (unless it is overridden with `--go-path` option); it then `go get`s the specified package and compiles it statically.
Then it generates an image manifest (using mostly default values) and leverages the [appc/spec](https://github.com/appc/spec) libraries to construct an ACI.

## TODO

Expand Down
117 changes: 117 additions & 0 deletions asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
)

func copyRegularFile(src, dest string) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defer close(srcFile)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And BTW. srcFile.Close() ;)

defer srcFile.Close()
destFile, err := os.Create(dest)
if err != nil {
return err
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defer close(destFile)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching it.

defer destFile.Close()
if _, err := io.Copy(destFile, srcFile); err != nil {
return err
}
return nil
}

func copySymlink(src, dest string) error {
symTarget, err := os.Readlink(src)
if err != nil {
return err
}
if err := os.Symlink(symTarget, dest); err != nil {
return err
}
return nil
}

func copyTree(src, dest string) error {
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
rootLess := path[len(src):]
target := filepath.Join(dest, rootLess)
mode := info.Mode()
switch {
case mode.IsDir():
err := os.Mkdir(target, mode.Perm())
if err != nil {
return err
}
case mode.IsRegular():
if err := copyRegularFile(path, target); err != nil {
return err
}
case mode&os.ModeSymlink == os.ModeSymlink:
if err := copySymlink(path, target); err != nil {
return err
}
default:
return fmt.Errorf("Unsupported node %q in assets, only regular files, directories and symlinks are supported.", path, mode.String())
}
return nil
})
}

func replacePlaceholders(path string, placeholderMapping map[string]string) string {
Debug("Processing path: ", path)
newPath := path
for placeholder, replacement := range placeholderMapping {
newPath = strings.Replace(newPath, placeholder, replacement, -1)
}
Debug("Processed path: ", newPath)
return newPath
}

func validateAsset(ACIAsset, localAsset string) error {
if !filepath.IsAbs(ACIAsset) {
return fmt.Errorf("Wrong ACI asset: '%v' - ACI asset has to be absolute path", ACIAsset)
}
if !filepath.IsAbs(localAsset) {
return fmt.Errorf("Wrong local asset: '%v' - local asset has to be absolute path", localAsset)
}
fi, err := os.Stat(localAsset)
if err != nil {
return fmt.Errorf("Error stating %v: %v", localAsset, err)
}
if fi.Mode().IsDir() || fi.Mode().IsRegular() {
return nil
}
return fmt.Errorf("Can't handle local asset %v - not a file, not a dir", fi.Name())
}

func PrepareAssets(assets []string, rootfs string, placeholderMapping map[string]string) error {
for _, asset := range assets {
splitAsset := filepath.SplitList(asset)
if len(splitAsset) != 2 {
return fmt.Errorf("Malformed asset option: '%v' - expected two absolute paths separated with %v", asset, ListSeparator())
}
ACIAsset := replacePlaceholders(splitAsset[0], placeholderMapping)
localAsset := replacePlaceholders(splitAsset[1], placeholderMapping)
if err := validateAsset(ACIAsset, localAsset); err != nil {
return err
}
ACIAssetSubPath := filepath.Join(rootfs, filepath.Dir(ACIAsset))
err := os.MkdirAll(ACIAssetSubPath, 0755)
if err != nil {
return fmt.Errorf("Failed to create directory tree for asset '%v': %v", asset, err)
}
err = copyTree(localAsset, filepath.Join(rootfs, ACIAsset))
if err != nil {
return fmt.Errorf("Failed to copy assets for '%v': %v", asset, err)
}
}
return nil
}
Loading