From 1786a7965ebf4b3b2cdc2d6f3817185ee7fee00d Mon Sep 17 00:00:00 2001 From: wencaiwulue <895703375@qq.com> Date: Wed, 11 Jan 2023 21:37:42 +0800 Subject: [PATCH] feat: add sub-command upgrade --- .github/workflows/upload_release.yml | 1 + Makefile | 1 + cmd/kubevpn/cmds/upgarde_test.go | 47 +++++++++ cmd/kubevpn/cmds/upgrade.go | 38 ++++++++ go.mod | 132 +++++++++++++------------- go.sum | 8 +- pkg/upgrade/modle.go | 93 ++++++++++++++++++ pkg/upgrade/progressreader.go | 59 ++++++++++++ pkg/upgrade/upgrade.go | 137 +++++++++++++++++++++++++++ pkg/upgrade/upgrade_test.go | 34 +++++++ 10 files changed, 484 insertions(+), 66 deletions(-) create mode 100644 cmd/kubevpn/cmds/upgarde_test.go create mode 100644 cmd/kubevpn/cmds/upgrade.go create mode 100644 pkg/upgrade/modle.go create mode 100644 pkg/upgrade/progressreader.go create mode 100644 pkg/upgrade/upgrade.go create mode 100644 pkg/upgrade/upgrade_test.go diff --git a/.github/workflows/upload_release.yml b/.github/workflows/upload_release.yml index 45be176e..b39d09c4 100644 --- a/.github/workflows/upload_release.yml +++ b/.github/workflows/upload_release.yml @@ -33,6 +33,7 @@ jobs: - name: Build kubevpn-all-arch run: | git tag `echo ${{ github.event.client_payload.tag }} | sed 's/refs\/tags\///' | sed 's/\(.*\)-.*/\1/' | sed 's/-[0-9]*$//' || true` || true + export GitHubOAuthToken=${{ secrets.KUBEVPN_UPGRADE_OAUTH }} make all-kubevpn - name: Upload Release Asset diff --git a/Makefile b/Makefile index a818c882..4b95e259 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ LDFLAGS=--ldflags "\ -X ${FOLDER}/cmds.GitCommit=${GIT_COMMIT} \ -X ${FOLDER}/cmds.Branch=${BRANCH} \ -X ${FOLDER}/cmds.OsArch=${OS_ARCH} \ + -X ${FOLDER}/cmds.GitHubOAuthToken=${GitHubOAuthToken} \ " GO111MODULE=on diff --git a/cmd/kubevpn/cmds/upgarde_test.go b/cmd/kubevpn/cmds/upgarde_test.go new file mode 100644 index 00000000..970e97ac --- /dev/null +++ b/cmd/kubevpn/cmds/upgarde_test.go @@ -0,0 +1,47 @@ +package cmds + +import ( + "fmt" + "io" + "os" + "testing" + "time" +) + +func TestName111(t *testing.T) { + var processBar = []string{ + "00%: [ ]", + "05%: [## ]", + "10%: [#### ]", + "15%: [###### ]", + "20%: [######## ]", + "25%: [########## ]", + "30%: [############ ]", + "35%: [############## ]", + "40%: [################ ]", + "45%: [################## ]", + "50%: [#################### ]", + "55%: [###################### ]", + "60%: [######################## ]", + "65%: [########################## ]", + "70%: [############################ ]", + "75%: [############################## ]", + "80%: [################################ ]", + "85%: [################################## ]", + "90%: [#################################### ]", + "95%: [###################################### ]", + "100%:[##########################################]\n", + } + for idx, val := range processBar { + fmt.Printf("[%d:1H:2K] \r \a%s", idx, val) + time.Sleep(1 * time.Millisecond * 200) + } +} + +func TestAAAAA(t *testing.T) { + writer := io.MultiWriter(os.Stdout, os.Stderr) + for i := 0; i < 10; i++ { + fmt.Fprintf(writer, "\r[%d:1H:2K]", i) + time.Sleep(1 * time.Millisecond * 200) + } +} diff --git a/cmd/kubevpn/cmds/upgrade.go b/cmd/kubevpn/cmds/upgrade.go new file mode 100644 index 00000000..e2c57f89 --- /dev/null +++ b/cmd/kubevpn/cmds/upgrade.go @@ -0,0 +1,38 @@ +package cmds + +import ( + "net/http" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "golang.org/x/oauth2" + + "github.com/wencaiwulue/kubevpn/pkg/upgrade" +) + +// GitHubOAuthToken +// --ldflags -X +var ( + GitHubOAuthToken = "" +) + +var upgradeCmd = &cobra.Command{ + Use: "upgrade", + Short: "Upgrade KubeVPN version", + Long: `Upgrade KubeVPN version, automatically download latest KubeVPN from GitHub`, + Run: func(cmd *cobra.Command, args []string) { + println(GitHubOAuthToken) + var client = http.DefaultClient + if GitHubOAuthToken != "" { + client = oauth2.NewClient(cmd.Context(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: GitHubOAuthToken, TokenType: "Bearer"})) + } + err := upgrade.Main(Version, client) + if err != nil { + log.Fatal(err) + } + }, +} + +func init() { + RootCmd.AddCommand(upgradeCmd) +} diff --git a/go.mod b/go.mod index 683f3977..6f167c8c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/docker/libcontainer v2.2.1+incompatible github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 github.com/fsnotify/fsnotify v1.5.4 - github.com/go-openapi/jsonpointer v0.19.5 + github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/miekg/dns v1.0.14 github.com/milosgajdos/tenus v0.0.3 github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 @@ -17,13 +17,13 @@ require ( github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/spf13/cobra v1.4.0 golang.org/x/net v0.0.0-20220722155237-a158d28d115b - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f + golang.org/x/sys v0.1.0 golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434 golang.zx2c4.com/wireguard/windows v0.4.10 - google.golang.org/appengine v1.6.7 + google.golang.org/appengine v1.6.7 // indirect google.golang.org/grpc v1.47.0 google.golang.org/protobuf v1.28.0 - gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/api v0.25.0 k8s.io/apimachinery v0.25.0 k8s.io/cli-runtime v0.24.2 @@ -33,72 +33,76 @@ require ( ) require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 - github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd - github.com/PuerkitoBio/purell v1.1.1 - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 - github.com/census-instrumentation/opencensus-proto v0.2.1 - github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 - github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 github.com/containernetworking/cni v1.1.2 - github.com/davecgh/go-spew v1.1.1 - github.com/emicklei/go-restful/v3 v3.8.0 - github.com/envoyproxy/protoc-gen-validate v0.1.0 - github.com/evanphx/json-patch v4.12.0+incompatible - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d - github.com/fatih/camelcase v1.0.0 - github.com/fvbommel/sortorder v1.0.1 - github.com/go-errors/errors v1.0.1 - github.com/go-logr/logr v1.2.3 - github.com/go-openapi/jsonreference v0.19.5 - github.com/go-openapi/swag v0.19.14 - github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 - github.com/google/btree v1.0.1 - github.com/google/gnostic v0.5.7-v3refs - github.com/google/go-cmp v0.5.8 - github.com/google/gofuzz v1.1.0 - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/google/uuid v1.2.0 - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 - github.com/imdario/mergo v0.3.12 - github.com/inconshreveable/mousetrap v1.0.0 - github.com/josharian/intern v1.0.0 - github.com/json-iterator/go v1.1.12 - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de - github.com/mailru/easyjson v0.7.6 + github.com/hashicorp/go-version v1.6.0 github.com/mattbaird/jsonpatch v0.0.0-20200820163806-098863c1fc24 - github.com/mitchellh/go-wordwrap v1.0.0 - github.com/moby/spdystream v0.2.0 - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd - github.com/modern-go/reflect2 v1.0.2 - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 - github.com/peterbourgon/diskv v2.0.1+incompatible - github.com/pmezard/go-difflib v1.0.0 - github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 - github.com/russross/blackfriday v1.5.2 - github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 - github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd + golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 - golang.org/x/text v0.3.7 - golang.org/x/time v0.0.0-20220609170525-579cf78fd858 - golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 - google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 - gopkg.in/inf.v0 v0.9.1 - gopkg.in/yaml.v3 v3.0.1 - k8s.io/component-base v0.25.0 - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 - sigs.k8s.io/kustomize/api v0.11.4 - sigs.k8s.io/kustomize/kyaml v0.13.6 - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 sigs.k8s.io/yaml v1.3.0 ) -require github.com/onsi/ginkgo v1.16.5 // indirect +require ( + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/census-instrumentation/opencensus-proto v0.2.1 // indirect + github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/camelcase v1.0.0 // indirect + github.com/fvbommel/sortorder v1.0.1 // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.2.0 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect + github.com/russross/blackfriday v1.5.2 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect + go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect + golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/component-base v0.25.0 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/kustomize/api v0.11.4 // indirect + sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect +) diff --git a/go.sum b/go.sum index 5ecff15c..0a700082 100644 --- a/go.sum +++ b/go.sum @@ -281,6 +281,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -536,6 +538,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a h1:tlXy25amD5A7gOfbXdqCGN5k8ESEed/Ee1E5RcrYnqU= +golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -704,8 +708,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= diff --git a/pkg/upgrade/modle.go b/pkg/upgrade/modle.go new file mode 100644 index 00000000..992f3c2d --- /dev/null +++ b/pkg/upgrade/modle.go @@ -0,0 +1,93 @@ +package upgrade + +type RootEntity struct { + Url string `json:"url"` + AssetsUrl string `json:"assets_url"` + UploadUrl string `json:"upload_url"` + HtmlUrl string `json:"html_url"` + Id int64 `json:"id"` + NodeId string `json:"node_id"` + TagName string `json:"tag_name"` + TargetCommitish string `json:"target_commitish"` + Name string `json:"name"` + Draft bool `json:"draft"` + Prerelease bool `json:"prerelease"` + CreatedAt string `json:"created_at"` + PublishedAt string `json:"published_at"` + Assets []AssetsEntity `json:"assets"` + TarballUrl string `json:"tarball_url"` + ZipballUrl string `json:"zipball_url"` + Body string `json:"body"` + Reactions ReactionsEntity `json:"reactions"` +} + +type AuthorEntity struct { + Login string `json:"login"` + Id int64 `json:"id"` + NodeId string `json:"node_id"` + AvatarUrl string `json:"avatar_url"` + GravatarId string `json:"gravatar_id"` + Url string `json:"url"` + HtmlUrl string `json:"html_url"` + FollowersUrl string `json:"followers_url"` + FollowingUrl string `json:"following_url"` + GistsUrl string `json:"gists_url"` + StarredUrl string `json:"starred_url"` + SubscriptionsUrl string `json:"subscriptions_url"` + OrganizationsUrl string `json:"organizations_url"` + ReposUrl string `json:"repos_url"` + EventsUrl string `json:"events_url"` + ReceivedEventsUrl string `json:"received_events_url"` + Type string `json:"type"` + SiteAdmin bool `json:"site_admin"` +} + +type AssetsEntity struct { + Url string `json:"url"` + Id int64 `json:"id"` + NodeId string `json:"node_id"` + Name string `json:"name"` + Label string `json:"label"` + Uploader UploaderEntity `json:"uploader"` + ContentType string `json:"content_type"` + State string `json:"state"` + Size int64 `json:"size"` + DownloadCount int64 `json:"download_count"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + BrowserDownloadUrl string `json:"browser_download_url"` +} + +type UploaderEntity struct { + Login string `json:"login"` + Id int64 `json:"id"` + NodeId string `json:"node_id"` + AvatarUrl string `json:"avatar_url"` + GravatarId string `json:"gravatar_id"` + Url string `json:"url"` + HtmlUrl string `json:"html_url"` + FollowersUrl string `json:"followers_url"` + FollowingUrl string `json:"following_url"` + GistsUrl string `json:"gists_url"` + StarredUrl string `json:"starred_url"` + SubscriptionsUrl string `json:"subscriptions_url"` + OrganizationsUrl string `json:"organizations_url"` + ReposUrl string `json:"repos_url"` + EventsUrl string `json:"events_url"` + ReceivedEventsUrl string `json:"received_events_url"` + Type string `json:"type"` + SiteAdmin bool `json:"site_admin"` +} + +type ReactionsEntity struct { + Url string `json:"url"` + TotalCount int64 `json:"total_count"` + Normal1 int64 `json:"+1"` + Normal11 int64 `json:"-1"` + Laugh int64 `json:"laugh"` + Hooray int64 `json:"hooray"` + Confused int64 `json:"confused"` + Heart int64 `json:"heart"` + Rocket int64 `json:"rocket"` + Eyes int64 `json:"eyes"` +} diff --git a/pkg/upgrade/progressreader.go b/pkg/upgrade/progressreader.go new file mode 100644 index 00000000..f7279efb --- /dev/null +++ b/pkg/upgrade/progressreader.go @@ -0,0 +1,59 @@ +package upgrade + +import ( + "io" + "sync" + "sync/atomic" +) + +type progress struct { + sync.Mutex + updates chan<- Update + lastUpdate *Update +} + +type Update struct { + Total int64 + Complete int64 + Error error +} + +func (p *progress) total(delta int64) { + atomic.AddInt64(&p.lastUpdate.Total, delta) +} + +func (p *progress) complete(delta int64) { + p.Lock() + defer p.Unlock() + p.updates <- Update{ + Total: p.lastUpdate.Total, + Complete: atomic.AddInt64(&p.lastUpdate.Complete, delta), + } +} + +func (p *progress) err(err error) error { + if err != nil && p.updates != nil { + p.updates <- Update{Error: err} + } + return err +} + +type progressReader struct { + rc io.ReadCloser + + count *int64 // number of bytes this reader has read, to support resetting on retry. + progress *progress +} + +func (r *progressReader) Read(b []byte) (int, error) { + n, err := r.rc.Read(b) + if err != nil { + return n, err + } + atomic.AddInt64(r.count, int64(n)) + // TODO: warn/debug log if sending takes too long, or if sending is blocked while context is canceled. + r.progress.complete(int64(n)) + return n, nil +} + +func (r *progressReader) Close() error { return r.rc.Close() } diff --git a/pkg/upgrade/upgrade.go b/pkg/upgrade/upgrade.go new file mode 100644 index 00000000..3650e3fa --- /dev/null +++ b/pkg/upgrade/upgrade.go @@ -0,0 +1,137 @@ +package upgrade + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "runtime" + + goversion "github.com/hashicorp/go-version" + "k8s.io/utils/pointer" +) + +// Main +// 1) get current binary version +// 2) get the latest version +// 3) compare two version decide needs to download or not +// 4) download newer version +// 5) chmod +x, move old to /temp, move new to CURRENT_FOLDER +func Main(current string, client *http.Client) error { + version, url, err := getManifest(client) + if err != nil { + return err + } + cVersion, err := goversion.NewVersion(current) + if err != nil { + return err + } + dVersion, err := goversion.NewVersion(version) + if err != nil { + return err + } + if cVersion.GreaterThanOrEqual(dVersion) { + fmt.Println("current version is bigger than latest version, don't needs to download") + return nil + } + var temp *os.File + temp, err = os.CreateTemp("", "") + if err != nil { + return err + } + err = download(client, url, temp.Name()) + if err != nil { + return err + } + err = os.Chmod(temp.Name(), 0755) + if err != nil { + return err + } + var curFolder string + curFolder, err = os.Executable() + if err != nil { + return err + } + createTemp, err := os.CreateTemp("", "") + err = os.Remove(createTemp.Name()) + err = os.Rename(curFolder, createTemp.Name()) + err = os.Rename(temp.Name(), curFolder) + return err +} + +func getManifest(httpCli *http.Client) (version string, url string, err error) { + var resp *http.Response + resp, err = httpCli.Get("https://api.github.com/repos/wencaiwulue/kubevpn/releases/latest") + if err != nil { + err = fmt.Errorf("failed to resp latest version of KubeVPN, err: %v", err) + return + } + all, err := io.ReadAll(resp.Body) + if err != nil { + err = fmt.Errorf("failed to resp latest version of KubeVPN, err: %v", err) + return + } + var m RootEntity + err = json.Unmarshal(all, &m) + if err != nil { + err = fmt.Errorf("failed to resp latest version of KubeVPN, err: %v", err) + return + } + version = m.TagName + for _, asset := range m.Assets { + name := fmt.Sprintf("%s-%s-%s", "kubevpn", runtime.GOOS, runtime.GOARCH) + if name == asset.Name { + url = asset.BrowserDownloadUrl + break + } + } + if len(url) == 0 { + err = fmt.Errorf("failed to resp latest version url of KubeVPN") + return + } + return +} + +// https://api.github.com/repos/wencaiwulue/kubevpn/releases +// https://github.com/wencaiwulue/kubevpn/releases/download/v1.1.13/kubevpn-windows-arm64.exe +func download(client *http.Client, url string, filename string) error { + get, err := client.Get(url) + if err != nil { + return err + } + pr := make(chan Update, 1) + p := &progressReader{ + rc: get.Body, + count: pointer.Int64(0), + progress: &progress{ + updates: pr, + lastUpdate: &Update{}, + }, + } + p.progress.total(get.ContentLength) + go func() { + var last string + for { + select { + case u, ok := <-pr: + if ok { + per := float32(u.Complete) / float32(u.Total) * 100 + s := fmt.Sprintf("%.0f%%", per) + if last != s { + fmt.Printf(fmt.Sprintf("\r%s%%", s)) + last = s + } + } + } + } + }() + var f *os.File + f, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + buf := make([]byte, 10<<(10*2)) // 10M + _, err = io.CopyBuffer(f, p, buf) + return err +} diff --git a/pkg/upgrade/upgrade_test.go b/pkg/upgrade/upgrade_test.go new file mode 100644 index 00000000..a4937a98 --- /dev/null +++ b/pkg/upgrade/upgrade_test.go @@ -0,0 +1,34 @@ +package upgrade + +import ( + "fmt" + "testing" + + goversion "github.com/hashicorp/go-version" + "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" +) + +func TestCompare(t *testing.T) { + version, err := goversion.NewVersion("v1.1.12") + assert.Nil(t, err) + + newVersion, err := goversion.NewVersion("v1.1.13") + assert.Nil(t, err) + + assert.True(t, version.LessThan(newVersion)) +} + +func TestGetManifest(t *testing.T) { + client := oauth2.NewClient(nil, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "", TokenType: "Bearer"})) + manifest, url, err := getManifest(client) + assert.Nil(t, err) + assert.True(t, manifest != "") + assert.True(t, url != "") +} + +func TestPercentage(t *testing.T) { + per := float32(1) / float32(100) * 100 + s := fmt.Sprintf("%.0f%%", per) + fmt.Printf(fmt.Sprintf("\r%s%%", s)) +}