Skip to content

Commit 7d5dd76

Browse files
committed
Add support for running on Windows (aws#354)
* Support building and running the ecs-cli on Windows. * Updated the staging script to support deploying binaries for windows. * Updated the publishing script to support releasing binaries for windows. * Added Windows binary links to README.
1 parent ab06004 commit 7d5dd76

8 files changed

Lines changed: 182 additions & 15 deletions

File tree

‎Makefile‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
2020
LOCAL_BINARY := bin/local/ecs-cli
2121
LINUX_BINARY := bin/linux-amd64/ecs-cli
2222
DARWIN_BINARY := bin/darwin-amd64/ecs-cli
23+
WINDOWS_BINARY := bin/windows-amd64/ecs-cli.exe
2324
LOCAL_PATH := $(ROOT)/scripts:${PATH}
2425

2526
.PHONY: build
@@ -43,6 +44,8 @@ generate-deps:
4344
go get github.com/golang/mock/mockgen
4445
go get golang.org/x/tools/cmd/goimports
4546

47+
.PHONY: windows-build
48+
windows-build: $(WINDOWS_BINARY)
4649

4750
.PHONY: docker-build
4851
docker-build:
@@ -56,6 +59,11 @@ docker-build:
5659
--env GOPATH=/usr/src/app \
5760
--env ECS_RELEASE=$(ECS_RELEASE) \
5861
golang:1.8 make $(DARWIN_BINARY)
62+
docker run -v $(shell pwd):/usr/src/app/src/github.com/aws/amazon-ecs-cli \
63+
--workdir=/usr/src/app/src/github.com/aws/amazon-ecs-cli \
64+
--env GOPATH=/usr/src/app \
65+
--env ECS_RELEASE=$(ECS_RELEASE) \
66+
golang:1.8 make $(WINDOWS_BINARY)
5967

6068
.PHONY: docker-test
6169
docker-test:
@@ -66,7 +74,13 @@ docker-test:
6674
golang:1.8 make test
6775

6876
.PHONY: supported-platforms
69-
supported-platforms: $(LINUX_BINARY) $(DARWIN_BINARY)
77+
supported-platforms: $(LINUX_BINARY) $(DARWIN_BINARY) $(WINDOWS_BINARY)
78+
79+
$(WINDOWS_BINARY): $(SOURCES)
80+
@mkdir -p ./bin/windows-amd64
81+
TARGET_GOOS=windows GOARCH=amd64 ./scripts/build_binary.sh ./bin/windows-amd64
82+
mv ./bin/windows-amd64/ecs-cli ./bin/windows-amd64/ecs-cli.exe
83+
@echo "Built ecs-cli.exe for windows"
7084

7185
$(LINUX_BINARY): $(SOURCES)
7286
@mkdir -p ./bin/linux-amd64

‎README.md‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ verify the integrity of your download.
4848
* [https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-latest](https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-latest)
4949
* [https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-latest.md5](https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-latest.md5)
5050
* Windows:
51-
* (Not yet implemented)
51+
* [https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-windows-amd64-latest.exe](https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-windows-amd64-latest.exe)
52+
* [https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-windows-amd64-latest.md5](https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-windows-amd64-latest.md5)
5253

5354
#### Download Links for within China
5455

@@ -60,8 +61,9 @@ As of v0.6.2 the ECS CLI supports the cn-north-1 region in China. The following
6061
* Macintosh:
6162
* [https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-darwin-amd64-latest](https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-darwin-amd64-latest)
6263
* [https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-darwin-amd64-latest.md5](https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-darwin-amd64-latest.md5)
63-
* Windows:
64-
* (Not yet implemented)
64+
* Windows:
65+
* [https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-windows-amd64-latest.exe](https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-windows-amd64-latest.exe)
66+
* [https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-windows-amd64-latest.md5](https://s3.cn-north-1.amazonaws.com.cn/amazon-ecs-cli/ecs-cli-windows-amd64-latest.md5)
6567

6668
### Download specific version
6769
Using the URLs above, replace `latest` with the desired tag, for example `v0.4.1`. After downloading, remember to rename the binary file to `ecs-cli`.

‎ecs-cli/modules/config/destination.go‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package config
1616
import (
1717
"os"
1818
"path/filepath"
19+
"runtime"
1920

2021
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils"
2122
"github.com/pkg/errors"
@@ -28,6 +29,18 @@ type Destination struct {
2829
Mode *os.FileMode
2930
}
3031

32+
// getOSName returns runtime.GOOS
33+
// In unit tests it can be mocked
34+
var getOSName = func() string {
35+
return runtime.GOOS
36+
}
37+
38+
// GetWindowsBaseDataPath returns the correct path to append
39+
// to a user home directory to store application data.
40+
func GetWindowsBaseDataPath() string {
41+
return filepath.Join("AppData", "local", "ecs")
42+
}
43+
3144
// GetFilePermissions is a utility method that gets permissions of a file.
3245
func GetFilePermissions(fileName string) (*os.FileMode, error) {
3346
fileInfo, err := os.Stat(fileName)
@@ -49,7 +62,10 @@ func NewDefaultDestination() (*Destination, error) {
4962
if err != nil {
5063
return nil, err
5164
}
65+
path := filepath.Join(homeDir, ".ecs")
66+
if getOSName() == "windows" {
67+
path = filepath.Join(homeDir, GetWindowsBaseDataPath())
68+
}
5269

53-
// TODO: Move to const.
54-
return &Destination{Path: filepath.Join(homeDir, ".ecs"), Mode: mode}, nil
70+
return &Destination{Path: path, Mode: mode}, nil
5571
}

‎ecs-cli/modules/config/destination_test.go‎

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,68 @@ package config
1616
import (
1717
"io/ioutil"
1818
"os"
19-
"strings"
19+
"path/filepath"
2020
"testing"
2121

2222
"github.com/stretchr/testify/assert"
2323
)
2424

25-
func TestNewDefaultDestination(t *testing.T) {
25+
func TestNewDefaultDestinationLinux(t *testing.T) {
26+
// Mock GetOSName in the test, then reset it after the test
27+
oldGetOSName := getOSName
28+
getOSName = func() string {
29+
return "linux"
30+
}
31+
defer func() { getOSName = oldGetOSName }()
32+
33+
// Create a temprorary directory for the dummy ecs config
34+
tempDirName, err := ioutil.TempDir("", "test")
35+
if err != nil {
36+
t.Fatal("Error while creating the dummy ecs config directory")
37+
}
38+
defer os.Remove(tempDirName)
39+
40+
os.Setenv("HOME", tempDirName)
41+
defer os.Unsetenv("HOME")
42+
43+
dest, err := newDefaultDestination()
44+
assert.NoError(t, err, "Unexpected error creating new config path")
45+
assert.Equal(t, filepath.Join(tempDirName, ".ecs"), dest.Path)
46+
assert.True(t, dest.Mode.IsDir(), "Expected user home directory to be in directory mode")
47+
}
48+
49+
func TestNewDefaultDestinationDarwin(t *testing.T) {
50+
// Mock GetOSName in the test, then reset it after the test
51+
oldGetOSName := getOSName
52+
getOSName = func() string {
53+
return "darwin"
54+
}
55+
defer func() { getOSName = oldGetOSName }()
56+
57+
// Create a temprorary directory for the dummy ecs config
58+
tempDirName, err := ioutil.TempDir("", "test")
59+
if err != nil {
60+
t.Fatal("Error while creating the dummy ecs config directory")
61+
}
62+
defer os.Remove(tempDirName)
63+
64+
os.Setenv("HOME", tempDirName)
65+
defer os.Unsetenv("HOME")
66+
67+
dest, err := newDefaultDestination()
68+
assert.NoError(t, err, "Unexpected error creating new config path")
69+
assert.Equal(t, filepath.Join(tempDirName, ".ecs"), dest.Path)
70+
assert.True(t, dest.Mode.IsDir(), "Expected user home directory to be in directory mode")
71+
}
72+
73+
func TestNewDefaultDestinationWindows(t *testing.T) {
74+
// Mock GetOSName in the test, then reset it after the test
75+
oldGetOSName := getOSName
76+
getOSName = func() string {
77+
return "windows"
78+
}
79+
defer func() { getOSName = oldGetOSName }()
80+
2681
// Create a temprorary directory for the dummy ecs config
2782
tempDirName, err := ioutil.TempDir("", "test")
2883
if err != nil {
@@ -35,8 +90,6 @@ func TestNewDefaultDestination(t *testing.T) {
3590

3691
dest, err := NewDefaultDestination()
3792
assert.NoError(t, err, "Unexpected error creating new config path")
38-
assert.Condition(t, func() bool {
39-
return strings.HasSuffix(dest.Path, "ecs")
40-
}, "Invalid suffix for ecs config path")
93+
assert.Equal(t, filepath.Join(tempDirName, "AppData", "local", "ecs"), dest.Path)
4194
assert.True(t, dest.Mode.IsDir(), "Expected user home directory to be in directory mode")
4295
}

‎ecs-cli/modules/utils/cache/fs_cache.go‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,19 @@ import (
1717
"encoding/gob"
1818
"os"
1919
"path/filepath"
20+
"runtime"
2021

22+
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/config"
2123
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils"
2224
)
2325

2426
// available for injecting mocks for testing
2527
var osOpen = os.Open
2628
var osCreate = os.Create
2729
var osMkdirAll = os.MkdirAll
30+
var getOSName = func() string {
31+
return runtime.GOOS
32+
}
2833

2934
// rw only for the user in-line with how most programs setup their cache
3035
// directories. Also, this directory could container sensitive-ish files due to
@@ -39,8 +44,11 @@ func cacheDir(name string) (string, error) {
3944
if err != nil {
4045
return "", err
4146
}
42-
// TODO, speciailize for windows, possibly OS X
43-
return filepath.Join(homedir, ".cache", cachePrefix, name), nil
47+
path := filepath.Join(homedir, ".cache", cachePrefix, name)
48+
if getOSName() == "windows" {
49+
path = filepath.Join(homedir, config.GetWindowsBaseDataPath(), "cache", name)
50+
}
51+
return path, nil
4452
}
4553

4654
type fsCache struct {

‎ecs-cli/modules/utils/cache/fs_cache_test.go‎

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package cache
1616
import (
1717
"io/ioutil"
1818
"os"
19+
"path/filepath"
1920
"testing"
2021

2122
"github.com/stretchr/testify/assert"
@@ -44,8 +45,77 @@ func TestCacheCreatesDir(t *testing.T) {
4445
}
4546
}
4647

48+
func TestCacheDirLinux(t *testing.T) {
49+
// Mock GetOSName in the test, then reset it after the test
50+
oldGetOSName := getOSName
51+
getOSName = func() string {
52+
return "linux"
53+
}
54+
defer func() { getOSName = oldGetOSName }()
55+
56+
// Create a temporary directory for the dummy ecs config
57+
tempDirName, err := ioutil.TempDir("", "test")
58+
if err != nil {
59+
t.Fatal("Error while creating the dummy ecs config directory")
60+
}
61+
defer os.Remove(tempDirName)
62+
63+
os.Setenv("HOME", tempDirName)
64+
defer os.Unsetenv("HOME")
65+
66+
path, err := cacheDir("name")
67+
assert.NoError(t, err, "Unexpected error creating new config path")
68+
assert.Equal(t, filepath.Join(tempDirName, ".cache", cachePrefix, "name"), path)
69+
}
70+
71+
func TestCacheDirDarwin(t *testing.T) {
72+
// Mock GetOSName in the test, then reset it after the test
73+
oldGetOSName := getOSName
74+
getOSName = func() string {
75+
return "darwin"
76+
}
77+
defer func() { getOSName = oldGetOSName }()
78+
79+
// Create a temporary directory for the dummy ecs config
80+
tempDirName, err := ioutil.TempDir("", "test")
81+
if err != nil {
82+
t.Fatal("Error while creating the dummy ecs config directory")
83+
}
84+
defer os.Remove(tempDirName)
85+
86+
os.Setenv("HOME", tempDirName)
87+
defer os.Unsetenv("HOME")
88+
89+
path, err := cacheDir("name")
90+
assert.NoError(t, err, "Unexpected error creating new config path")
91+
assert.Equal(t, filepath.Join(tempDirName, ".cache", cachePrefix, "name"), path)
92+
}
93+
94+
func TestCacheDirWindows(t *testing.T) {
95+
// Mock GetOSName in the test, then reset it after the test
96+
oldGetOSName := getOSName
97+
getOSName = func() string {
98+
return "windows"
99+
}
100+
defer func() { getOSName = oldGetOSName }()
101+
102+
// Create a temporary directory for the dummy ecs config
103+
tempDirName, err := ioutil.TempDir("", "test")
104+
if err != nil {
105+
t.Fatal("Error while creating the dummy ecs config directory")
106+
}
107+
defer os.Remove(tempDirName)
108+
109+
os.Setenv("HOME", tempDirName)
110+
defer os.Unsetenv("HOME")
111+
112+
path, err := cacheDir("name")
113+
assert.NoError(t, err, "Unexpected error creating new config path")
114+
assert.Equal(t, filepath.Join(tempDirName, "AppData", "local", "ecs", "cache", "name"), path)
115+
}
116+
47117
func tempDir(t *testing.T) string {
48-
// Create a temprorary directory for the dummy ecs config
118+
// Create a temporary directory for the dummy ecs config
49119
tempDirName, err := ioutil.TempDir("", "test")
50120
assert.NoError(t, err, "Unexpected error while creating the dummy ecs config directory")
51121
return tempDirName

‎scripts/publish-staged‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ publish_s3() {
4848
echo "Publishing as ecs-cli-darwin-amd64-${tag}"
4949
dexec s3_pull_push "s3://${STAGE_S3_BUCKET}/ecs-cli-darwin-amd64-${tag}" "s3://${PUBLISH_S3_BUCKET}/ecs-cli-darwin-amd64-${tag}"
5050
dexec s3_pull_push "s3://${STAGE_S3_BUCKET}/ecs-cli-darwin-amd64-${tag}.md5" "s3://${PUBLISH_S3_BUCKET}/ecs-cli-darwin-amd64-${tag}.md5"
51+
echo "Publishing as ecs-cli-windows-amd64-${tag}.exe"
52+
dexec s3_pull_push "s3://${STAGE_S3_BUCKET}/ecs-cli-windows-amd64-${tag}" "s3://${PUBLISH_S3_BUCKET}/ecs-cli-windows-amd64-${tag}.exe"
53+
dexec s3_pull_push "s3://${STAGE_S3_BUCKET}/ecs-cli-windows-amd64-${tag}.md5" "s3://${PUBLISH_S3_BUCKET}/ecs-cli-windows-amd64-${tag}.md5"
5154
done
5255
}
5356

‎scripts/stage.sh‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ if ! ${ALLOW_DIRTY}; then
103103
fi
104104

105105
make docker-build
106+
mv bin/windows-amd64/ecs-cli.exe bin/windows-amd64/ecs-cli
106107

107-
for platform in "linux-amd64" "darwin-amd64"; do
108+
for platform in "linux-amd64" "darwin-amd64" "windows-amd64"; do
108109
artifact="bin/${platform}/ecs-cli"
109110
artifact_md5="$(mktemp)"
110111
md5sum "${artifact}" | sed 's/ .*//' > "${artifact_md5}"

0 commit comments

Comments
 (0)