From 01217aa9bdda2f959038272a8121cf977f68a14f Mon Sep 17 00:00:00 2001 From: Fabian Ruff Date: Thu, 16 Nov 2017 10:13:38 +0100 Subject: [PATCH] /api/v1/clusters/info redirects to github releases for downloads --- Makefile | 2 +- ci/task_cli.yaml | 5 +- pkg/api/handlers/get_cluster_info.go | 80 +++++++++-- pkg/api/handlers/get_cluster_info_test.go | 162 ++++++++++++++++++++++ pkg/api/handlers/info.go | 4 +- pkg/apis/kubernikus/factory.go | 2 +- pkg/controller/operator.go | 2 +- pkg/templates/ignition.go | 2 +- pkg/templates/ignition_test.go | 5 +- pkg/version/base.go | 4 +- pkg/wormhole/server.go | 2 +- 11 files changed, 243 insertions(+), 27 deletions(-) create mode 100644 pkg/api/handlers/get_cluster_info_test.go diff --git a/Makefile b/Makefile index 218fa4a0f7..8ad63092ec 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION ?= $(shell git rev-parse --verify HEAD) GOOS ?= $(shell go env | grep GOOS | cut -d'"' -f2) BINARIES := apiserver kubernikus kubernikusctl wormhole -LDFLAGS := -X github.com/sapcc/kubernikus/pkg/version.VERSION=$(VERSION) +LDFLAGS := -X github.com/sapcc/kubernikus/pkg/version.GitCommit=$(VERSION) GOFLAGS := -ldflags "$(LDFLAGS) -s -w" SRCDIRS := pkg cmd diff --git a/ci/task_cli.yaml b/ci/task_cli.yaml index e8655c5c14..71f8220f78 100644 --- a/ci/task_cli.yaml +++ b/ci/task_cli.yaml @@ -21,8 +21,9 @@ run: make bin/linux/kubernikusctl make bin/windows/kubernikusctl.exe upx bin/*/* - git rev-parse HEAD > $BINARIES/commitish - echo "1.0.0+"$(git rev-parse --short HEAD) > $BINARIES/tag + SHA=$(git rev-parse HEAD) + echo $SHA > $BINARIES/commitish + echo "1.0.0+$SHA" > $BINARIES/tag cp bin/darwin/kubernikusctl $BINARIES/kubernikusctl_darwin_amd64 cp bin/linux/kubernikusctl $BINARIES/kubernikusctl_linux_amd64 diff --git a/pkg/api/handlers/get_cluster_info.go b/pkg/api/handlers/get_cluster_info.go index 73df5f5eb1..aab3df501e 100644 --- a/pkg/api/handlers/get_cluster_info.go +++ b/pkg/api/handlers/get_cluster_info.go @@ -1,44 +1,100 @@ package handlers import ( + "encoding/json" "fmt" + "net/http" + "strings" + "sync" "github.com/go-openapi/runtime/middleware" "github.com/sapcc/kubernikus/pkg/api" "github.com/sapcc/kubernikus/pkg/api/models" "github.com/sapcc/kubernikus/pkg/api/rest/operations" + "github.com/sapcc/kubernikus/pkg/version" ) func NewGetClusterInfo(rt *api.Runtime) operations.GetClusterInfoHandler { - return &getClusterInfo{rt} + return &getClusterInfo{Runtime: rt, githubApiURL: "https://api.github.com"} } type getClusterInfo struct { *api.Runtime + links []models.Link + linkMutex sync.Mutex + githubApiURL string } func (d *getClusterInfo) Handle(params operations.GetClusterInfoParams, principal *models.Principal) middleware.Responder { + + links, err := d.getLinks() + if err != nil { + return NewErrorResponse(&operations.GetClusterInfoDefault{}, 500, err.Error()) + } + info := &models.KlusterInfo{ SetupCommand: createSetupCommand(principal), Binaries: []models.Binaries{ { - Name: "kubernikusctl", - Links: []models.Link{ - { - Platform: "darwin", - Link: "static/binaries/darwin/amd64/kubernikusctl", - }, - { - Platform: "linux", - Link: "static/binaries/linux/amd64/kubernikusctl", - }, - }, + Name: "kubernikusctl", + Links: links, }, }, } return operations.NewGetClusterInfoOK().WithPayload(info) } +func (d *getClusterInfo) getLinks() ([]models.Link, error) { + d.linkMutex.Lock() + defer d.linkMutex.Unlock() + if d.links != nil { + return d.links, nil + } + + release := "latest" + if version.GitCommit != "HEAD" { + release = fmt.Sprintf("%s-%s", version.VERSION, version.GitCommit) + } + resp, err := http.Get(fmt.Sprintf("%s/repos/sapcc/kubernikus/releases/%s", d.githubApiURL, release)) + if err != nil { + return nil, fmt.Errorf("Failed to fetch release %s: %s", release, err) + } + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("Failed to fetch release %s: %s", release, resp.Status) + } + var releaseResponse struct { + Assets []struct { + Name string + DownloadURL string `json:"browser_download_url"` + } + } + if err := json.NewDecoder(resp.Body).Decode(&releaseResponse); err != nil { + return nil, err + } + links := make([]models.Link, 0, 3) + for _, asset := range releaseResponse.Assets { + link := models.Link{Link: asset.DownloadURL} + switch { + case strings.Contains(asset.Name, "darwin"): + link.Platform = "darwin" + case strings.Contains(asset.Name, "linux"): + link.Platform = "linux" + case strings.Contains(asset.Name, "windows"): + link.Platform = "windows" + default: + //skip unknown assets + continue + } + links = append(links, link) + } + if len(links) == 0 { + return nil, fmt.Errorf("No downloads found for release %s", release) + } + d.links = links + return links, nil + +} + func createSetupCommand(principal *models.Principal) string { userName := principal.Name userDomainName := principal.Domain diff --git a/pkg/api/handlers/get_cluster_info_test.go b/pkg/api/handlers/get_cluster_info_test.go new file mode 100644 index 0000000000..1a967d46ad --- /dev/null +++ b/pkg/api/handlers/get_cluster_info_test.go @@ -0,0 +1,162 @@ +package handlers + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/sapcc/kubernikus/pkg/api/models" + "github.com/stretchr/testify/assert" +) + +func TestKubernikusctlDownloadLinks(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, releaseResponse) + })) + handler := getClusterInfo{githubApiURL: server.URL} + links, err := handler.getLinks() + assert.NoError(t, err) + expected := []models.Link{ + models.Link{Platform: "darwin", Link: "https://github.com/sapcc/kubernikus/releases/download/v20171115131940/kubernikusctl_darwin_amd64"}, + models.Link{Platform: "linux", Link: "https://github.com/sapcc/kubernikus/releases/download/v20171115131940/kubernikusctl_linux_amd64"}, + models.Link{Platform: "windows", Link: "https://github.com/sapcc/kubernikus/releases/download/v20171115131940/kubernikusctl_windows_amd64.exe"}, + } + assert.Equal(t, expected, links) + +} + +const releaseResponse = `{ + "url": "https://api.github.com/repos/sapcc/kubernikus/releases/8526436", + "assets_url": "https://api.github.com/repos/sapcc/kubernikus/releases/8526436/assets", + "upload_url": "https://uploads.github.com/repos/sapcc/kubernikus/releases/8526436/assets{?name,label}", + "html_url": "https://github.com/sapcc/kubernikus/releases/tag/v20171115131940", + "id": 8526436, + "tag_name": "v20171115131940", + "target_commitish": "10d14aeeec3e0ae063fd7e83ae121b9a10de876f", + "name": "20171115131940", + "draft": false, + "author": { + "login": "sapcc-bot", + "id": 23400221, + "avatar_url": "https://avatars2.githubusercontent.com/u/23400221?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sapcc-bot", + "html_url": "https://github.com/sapcc-bot", + "followers_url": "https://api.github.com/users/sapcc-bot/followers", + "following_url": "https://api.github.com/users/sapcc-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/sapcc-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sapcc-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sapcc-bot/subscriptions", + "organizations_url": "https://api.github.com/users/sapcc-bot/orgs", + "repos_url": "https://api.github.com/users/sapcc-bot/repos", + "events_url": "https://api.github.com/users/sapcc-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/sapcc-bot/received_events", + "type": "User", + "site_admin": false + }, + "prerelease": false, + "created_at": "2017-11-15T11:43:05Z", + "published_at": "2017-11-15T13:21:40Z", + "assets": [ + { + "url": "https://api.github.com/repos/sapcc/kubernikus/releases/assets/5353345", + "id": 5353345, + "name": "kubernikusctl_darwin_amd64", + "label": "", + "uploader": { + "login": "sapcc-bot", + "id": 23400221, + "avatar_url": "https://avatars2.githubusercontent.com/u/23400221?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sapcc-bot", + "html_url": "https://github.com/sapcc-bot", + "followers_url": "https://api.github.com/users/sapcc-bot/followers", + "following_url": "https://api.github.com/users/sapcc-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/sapcc-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sapcc-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sapcc-bot/subscriptions", + "organizations_url": "https://api.github.com/users/sapcc-bot/orgs", + "repos_url": "https://api.github.com/users/sapcc-bot/repos", + "events_url": "https://api.github.com/users/sapcc-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/sapcc-bot/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 6025872, + "download_count": 2, + "created_at": "2017-11-15T13:21:41Z", + "updated_at": "2017-11-15T13:21:52Z", + "browser_download_url": "https://github.com/sapcc/kubernikus/releases/download/v20171115131940/kubernikusctl_darwin_amd64" + }, + { + "url": "https://api.github.com/repos/sapcc/kubernikus/releases/assets/5353346", + "id": 5353346, + "name": "kubernikusctl_linux_amd64", + "label": "", + "uploader": { + "login": "sapcc-bot", + "id": 23400221, + "avatar_url": "https://avatars2.githubusercontent.com/u/23400221?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sapcc-bot", + "html_url": "https://github.com/sapcc-bot", + "followers_url": "https://api.github.com/users/sapcc-bot/followers", + "following_url": "https://api.github.com/users/sapcc-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/sapcc-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sapcc-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sapcc-bot/subscriptions", + "organizations_url": "https://api.github.com/users/sapcc-bot/orgs", + "repos_url": "https://api.github.com/users/sapcc-bot/repos", + "events_url": "https://api.github.com/users/sapcc-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/sapcc-bot/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 5648232, + "download_count": 1, + "created_at": "2017-11-15T13:21:52Z", + "updated_at": "2017-11-15T13:22:17Z", + "browser_download_url": "https://github.com/sapcc/kubernikus/releases/download/v20171115131940/kubernikusctl_linux_amd64" + }, + { + "url": "https://api.github.com/repos/sapcc/kubernikus/releases/assets/5353347", + "id": 5353347, + "name": "kubernikusctl_windows_amd64.exe", + "label": "", + "uploader": { + "login": "sapcc-bot", + "id": 23400221, + "avatar_url": "https://avatars2.githubusercontent.com/u/23400221?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/sapcc-bot", + "html_url": "https://github.com/sapcc-bot", + "followers_url": "https://api.github.com/users/sapcc-bot/followers", + "following_url": "https://api.github.com/users/sapcc-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/sapcc-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/sapcc-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/sapcc-bot/subscriptions", + "organizations_url": "https://api.github.com/users/sapcc-bot/orgs", + "repos_url": "https://api.github.com/users/sapcc-bot/repos", + "events_url": "https://api.github.com/users/sapcc-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/sapcc-bot/received_events", + "type": "User", + "site_admin": false + }, + "content_type": "application/octet-stream", + "state": "uploaded", + "size": 5641216, + "download_count": 1, + "created_at": "2017-11-15T13:22:17Z", + "updated_at": "2017-11-15T13:22:37Z", + "browser_download_url": "https://github.com/sapcc/kubernikus/releases/download/v20171115131940/kubernikusctl_windows_amd64.exe" + } + ], + "tarball_url": "https://api.github.com/repos/sapcc/kubernikus/tarball/v20171115131940", + "zipball_url": "https://api.github.com/repos/sapcc/kubernikus/zipball/v20171115131940", + "body": "" +}` diff --git a/pkg/api/handlers/info.go b/pkg/api/handlers/info.go index eb899bd5ee..a21e6543d0 100644 --- a/pkg/api/handlers/info.go +++ b/pkg/api/handlers/info.go @@ -18,9 +18,7 @@ type info struct { func (d *info) Handle(params operations.InfoParams) middleware.Responder { info := &models.Info{ - Version: version.VERSION, + Version: version.GitCommit, } return operations.NewInfoOK().WithPayload(info) } - - diff --git a/pkg/apis/kubernikus/factory.go b/pkg/apis/kubernikus/factory.go index 1a1838e58d..a93194dc3f 100644 --- a/pkg/apis/kubernikus/factory.go +++ b/pkg/apis/kubernikus/factory.go @@ -79,7 +79,7 @@ func (klusterFactory) KlusterFor(spec models.KlusterSpec) (*v1.Kluster, error) { } if k.Status.Version == "" { - k.Status.Version = version.VERSION + k.Status.Version = version.GitCommit } for _, nodePool := range k.Spec.NodePools { diff --git a/pkg/controller/operator.go b/pkg/controller/operator.go index 9a9f917fc2..2deee3ad31 100644 --- a/pkg/controller/operator.go +++ b/pkg/controller/operator.go @@ -167,7 +167,7 @@ func NewKubernikusOperator(options *KubernikusOperatorOptions) *KubernikusOperat } func (o *KubernikusOperator) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) { - fmt.Printf("Welcome to Kubernikus %v\n", version.VERSION) + fmt.Printf("Welcome to Kubernikus %v\n", version.GitCommit) kube.WaitForServer(o.Clients.Kubernetes, stopCh) diff --git a/pkg/templates/ignition.go b/pkg/templates/ignition.go index 2381322af6..2d58a5a9d0 100644 --- a/pkg/templates/ignition.go +++ b/pkg/templates/ignition.go @@ -87,7 +87,7 @@ func (i *ignition) GenerateNode(kluster *kubernikusv1.Kluster, secret *v1.Secret OpenstackLBSubnetID: kluster.Spec.Openstack.LBSubnetID, OpenstackRouterID: kluster.Spec.Openstack.RouterID, KubernikusImage: "sapcc/kubernikus", - KubernikusImageTag: version.VERSION, + KubernikusImageTag: version.GitCommit, } var buffer bytes.Buffer diff --git a/pkg/templates/ignition_test.go b/pkg/templates/ignition_test.go index 6b25f74876..047afd40b2 100644 --- a/pkg/templates/ignition_test.go +++ b/pkg/templates/ignition_test.go @@ -42,9 +42,6 @@ func TestGenerateNode(t *testing.T) { ObjectMeta: kluster.ObjectMeta, Data: secretData, } - bytes, err := Ignition.GenerateNode(&kluster, &secret) + _, err := Ignition.GenerateNode(&kluster, &secret) assert.NoError(t, err) - - fmt.Printf("bytes = %+v\n", string(bytes)) - } diff --git a/pkg/version/base.go b/pkg/version/base.go index d5bd9b4065..5814ebd75d 100644 --- a/pkg/version/base.go +++ b/pkg/version/base.go @@ -1,3 +1,5 @@ package version -var VERSION = "HEAD" +const VERSION = "1.0.0" + +var GitCommit = "HEAD" diff --git a/pkg/wormhole/server.go b/pkg/wormhole/server.go index 8d0befaaaf..8c24b9fc99 100644 --- a/pkg/wormhole/server.go +++ b/pkg/wormhole/server.go @@ -52,7 +52,7 @@ func NewServer(options *ServerOptions) (*Server, error) { } func (s *Server) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) { - fmt.Printf("Welcome to Wormhole %v\n", version.VERSION) + fmt.Printf("Welcome to Wormhole %v\n", version.GitCommit) kube.WaitForServer(s.client, stopCh)