-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding support for cross compatability darwin / linux
- Loading branch information
Showing
15 changed files
with
699 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,40 @@ | ||
-include .env | ||
# ########################################################## # | ||
# Makefile for Golang Project | ||
# Includes cross-compiling, installation, cleanup | ||
# ########################################################## # | ||
|
||
VERSION := $(shell git describe --tags) | ||
BUILD := $(shell git rev-parse --short HEAD) | ||
PROJECTNAME := $(shell basename "$(PWD)") | ||
# Check for required command tools to build or stop immediately | ||
EXECUTABLES = git go find pwd | ||
K := $(foreach exec,$(EXECUTABLES),\ | ||
$(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH))) | ||
|
||
# Go related variables. | ||
GOBASE := $(shell pwd) | ||
GOPATH := $(GOBASE)/vendor:$(GOBASE) | ||
GOBIN := $(GOBASE)/bin | ||
GOFILES := $(wildcard *.go) | ||
ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) | ||
|
||
# Use linker flags to provide version/build settings | ||
LDFLAGS=-ldflags "-X=main.Version=$(VERSION) -X=main.Build=$(BUILD)" | ||
BINARY=kubemngr | ||
VERSION=0.0.1 | ||
BUILD=`git rev-parse HEAD` | ||
PLATFORMS=darwin linux #windows | ||
ARCHITECTURES=386 amd64 | ||
|
||
# Redirect error output to a file, so we can show it in development mode. | ||
STDERR := /tmp/.$(PROJECTNAME)-stderr.txt | ||
# Setup linker flags option for build that interoperate with variable names in src code | ||
LDFLAGS=-ldflags "-X main.Version=${VERSION} -X main.Build=${BUILD}" | ||
|
||
# PID file will keep the process id of the server | ||
PID := /tmp/.$(PROJECTNAME).pid | ||
default: build | ||
|
||
# Make is verbose in Linux. Make it silent. | ||
MAKEFLAGS += --silent | ||
all: clean build_all install | ||
|
||
## install: Install missing dependencies. Runs `go get` internally. e.g; make install get=github.com/foo/bar | ||
install: go-get | ||
build: | ||
go build ${LDFLAGS} -o ${BINARY} | ||
|
||
## start: Start in development mode. Auto-starts when code changes. | ||
start: | ||
@bash -c "trap 'make stop' EXIT; $(MAKE) clean compile start-server watch run='make clean compile start-server'" | ||
build_all: | ||
$(foreach GOOS, $(PLATFORMS),\ | ||
$(foreach GOARCH, $(ARCHITECTURES), $(shell export GOOS=$(GOOS); export GOARCH=$(GOARCH); go build -v -o $(BINARY)-$(GOOS)-$(GOARCH)))) | ||
|
||
## stop: Stop development mode. | ||
stop: stop-server | ||
install: | ||
go install ${LDFLAGS} | ||
|
||
start-server: stop-server | ||
@echo " > $(PROJECTNAME) is available at $(ADDR)" | ||
@-$(GOBIN)/$(PROJECTNAME) 2>&1 & echo $$! > $(PID) | ||
@cat $(PID) | sed "/^/s/^/ \> PID: /" | ||
|
||
stop-server: | ||
@-touch $(PID) | ||
@-kill `cat $(PID)` 2> /dev/null || true | ||
@-rm $(PID) | ||
|
||
## watch: Run given command when code changes. e.g; make watch run="echo 'hey'" | ||
watch: | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) yolo -i . -e vendor -e bin -c "$(run)" | ||
|
||
restart-server: stop-server start-server | ||
|
||
## compile: Compile the binary. | ||
compile: | ||
@-touch $(STDERR) | ||
@-rm $(STDERR) | ||
@-$(MAKE) -s go-compile 2> $(STDERR) | ||
@cat $(STDERR) | sed -e '1s/.*/\nError:\n/' | sed 's/make\[.*/ /' | sed "/^/s/^/ /" 1>&2 | ||
|
||
## exec: Run given command, wrapped with custom GOPATH. e.g; make exec run="go test ./..." | ||
exec: | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) $(run) | ||
|
||
## clean: Clean build files. Runs `go clean` internally. | ||
# Remove only what we've created | ||
clean: | ||
@-rm $(GOBIN)/$(PROJECTNAME) 2> /dev/null | ||
@-$(MAKE) go-clean | ||
|
||
go-compile: go-get go-build | ||
|
||
go-build: | ||
@echo " > Building binary..." | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go build $(LDFLAGS) -o $(GOBIN)/$(PROJECTNAME) $(GOFILES) | ||
|
||
go-generate: | ||
@echo " > Generating dependency files..." | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go generate $(generate) | ||
|
||
go-get: | ||
@echo " > Checking if there is any missing dependencies..." | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go get $(get) | ||
|
||
go-install: | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES) | ||
|
||
go-clean: | ||
@echo " > Cleaning build cache" | ||
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean | ||
find ${ROOT_DIR} -name '${BINARY}[-?][a-zA-Z0-9]*[-?][a-zA-Z0-9]*' -delete | ||
|
||
.PHONY: help | ||
all: help | ||
help: Makefile | ||
@echo | ||
@echo " Choose a command run in "$(PROJECTNAME)":" | ||
@echo | ||
@sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' | ||
@echo | ||
.PHONY: check clean install build_all all |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
// +build darwin | ||
|
||
/* | ||
Copyright © 2019 NAME HERE <EMAIL ADDRESS> | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"os" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/dustin/go-humanize" | ||
_ "github.com/google/go-github/v27/github" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var sys, machine string | ||
|
||
type WriteCounter struct { | ||
Total uint64 | ||
} | ||
|
||
// installCmd represents the install command | ||
var installCmd = &cobra.Command{ | ||
Use: "install", | ||
Short: "A tool manage different kubectl versions inside a workspace.", | ||
//RunE: func(cmd *cobra.Command, args []string) error { | ||
// return errors.New("provide a kubectl version") | ||
//}, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
err := DownloadKubectl(args[0]) | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(installCmd) | ||
|
||
// Here you will define your flags and configuration settings. | ||
|
||
// Cobra supports Persistent Flags which will work for this command | ||
// and all subcommands, e.g.: | ||
// installCmd.PersistentFlags().String("foo", "", "A help for foo") | ||
|
||
// Cobra supports local flags which will only run when this command | ||
// is called directly, e.g.: | ||
// installCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") | ||
} | ||
|
||
//DownloadKubectl - download user specified version of kubectl | ||
func DownloadKubectl(version string) error { | ||
|
||
// TODO use tmp directory to download instead of kubemngr. | ||
// This was failing originally with the error: invalid cross-link device | ||
// filepath := "/tmp/" | ||
|
||
// TODO better sanity check for checking arg is valid | ||
if len(version) == 0 { | ||
log.Fatal(0) | ||
} | ||
|
||
// Get user home directory path | ||
homeDir, err := os.UserHomeDir() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Check if current version already exists | ||
_, err = os.Stat(homeDir + "/.kubemngr/kubectl-" + version) | ||
if err == nil { | ||
log.Printf("kubectl version %s already exists", version) | ||
return nil | ||
} | ||
|
||
// Create temp file of kubectl version in tmp directory | ||
out, err := os.Create(homeDir + "/.kubemngr/kubectl-" + version + ".tmp") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
defer out.Close() | ||
|
||
// Get OS information to filter download type i.e linux / darwin | ||
uname := GetOSInfo() | ||
|
||
// TODO refactor me | ||
// doesn't work on OSX | ||
if ArrayToString(uname.Sysname) == "Linux" { | ||
sys = "linux" | ||
} else if ArrayToString(uname.Sysname) == "Darwin" { | ||
sys = "darwin" | ||
} else { | ||
sys = "UNKNOWN" | ||
fmt.Println("Unknown system") | ||
} | ||
|
||
if ArrayToString(uname.Machine) == "arm" { | ||
machine = "arm" | ||
} else if ArrayToString(uname.Machine) == "arm64" { | ||
machine = "arm64" | ||
} else if ArrayToString(uname.Machine) == "x86_64" { | ||
machine = "amd64" | ||
} else { | ||
machine = "UNKNOWN" | ||
fmt.Println("Unknown machine") | ||
} | ||
|
||
url := "https://storage.googleapis.com/kubernetes-release/release/" + version + "/bin/" + sys + "/" + machine + "/kubectl" | ||
|
||
resp, err := http.Get(url) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
// Initialise WriteCounter and copy the contents of the response body to the tmp file | ||
counter := &WriteCounter{} | ||
_, err = io.Copy(out, io.TeeReader(resp.Body, counter)) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// The progress use the same line so print a new line once it's finished downloading | ||
fmt.Println() | ||
|
||
// Set executable permissions on the kubectl binary | ||
if err := os.Chmod(homeDir+"/.kubemngr/kubectl-"+version+".tmp", 0755); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Rename the tmp file back to the original file and store it in the kubemngr directory | ||
currentFilePath := homeDir + "/.kubemngr/kubectl-" + version + ".tmp" | ||
newFilePath := homeDir + "/.kubemngr/kubectl-" + version | ||
|
||
err = os.Rename(currentFilePath, newFilePath) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// GetOSInfo - Get operating system information of machine | ||
func GetOSInfo() syscall.Utsname { | ||
var uname syscall.Utsname | ||
|
||
if err := syscall.Uname(&uname); err != nil { | ||
fmt.Printf("Uname: %v", err) | ||
} | ||
|
||
return uname | ||
} | ||
|
||
func (wc *WriteCounter) Write(p []byte) (int, error) { | ||
n := len(p) | ||
wc.Total += uint64(n) | ||
wc.PrintProgress() | ||
return n, nil | ||
} | ||
|
||
// PrintProgress - Helper function to print progress of a download | ||
func (wc WriteCounter) PrintProgress() { | ||
// Clear the line by using a character return to go back to the start and remove | ||
// the remaining characters by filling it with spaces | ||
fmt.Printf("\r%s", strings.Repeat(" ", 50)) | ||
|
||
// Return again and print current status of download | ||
// We use the humanize package to print the bytes in a meaningful way (e.g. 10 MB) | ||
fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) | ||
} | ||
|
||
func ArrayToString(x [65]int8) string { | ||
var buf [65]byte | ||
for i, b := range x { | ||
buf[i] = byte(b) | ||
} | ||
str := string(buf[:]) | ||
if i := strings.Index(str, "\x00"); i != -1 { | ||
str = str[:i] | ||
} | ||
return str | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
// +build linux | ||
|
||
/* | ||
Copyright © 2019 NAME HERE <EMAIL ADDRESS> | ||
|
Oops, something went wrong.