aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.vscode/launch.json87
-rw-r--r--.vscode/settings.json26
-rw-r--r--LICENSE201
-rw-r--r--Makefile153
-rw-r--r--README.md13
-rw-r--r--cmd-exec.go166
-rw-r--r--cmd-misc.go66
-rw-r--r--cmd-projects.go209
-rw-r--r--cmd-sdks.go136
-rw-r--r--glide.yaml25
-rw-r--r--main.go263
-rw-r--r--utils.go73
13 files changed, 1423 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d33064e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+debug
+glide.lock
+bin/**
+tools/**
+vendor/**
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..6978301
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,87 @@
+{
+ "version": "0.2.0",
+ "configurations": [{
+ "name": "xds-cli (version)",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_APPNAME": "xds-cli",
+ "XDS_SERVER_URL": "localhost:8800",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["misc", "version"],
+ "showLog": false
+ },
+ {
+ "name": "xds-cli (list)",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_APPNAME": "xds-cli",
+ "XDS_SERVER_URL": "localhost:8800",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["sdks", "list"],
+ "showLog": false
+ },
+ {
+ "name": "xds-cli (add Projects)",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_APPNAME": "xds-cli",
+ "XDS_SERVER_URL": "localhost:8800",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["prj", "add",
+ "-type", "pm",
+ "-path", "/home/seb/xds-workspace/test1",
+ "-server-path", "/home/seb/xds-workspace/test1"
+ ],
+ "showLog": false
+ },
+ {
+ "name": "xds-cli (exec Projects)",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_APPNAME": "xds-cli",
+ "XDS_SERVER_URL": "localhost:8800",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["exec",
+ "-id", "IW7B4EE-DBY4Z74_Agent-TCF",
+ "-rpath", "build",
+ "pwd && ls .."
+ ],
+ "showLog": false
+ },
+ {
+ "name": "xds-cli (with xds-config.env)",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_APPNAME": "xds-cli",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["-c", "xds-config-sample.env", "sdks", "ls"],
+ "showLog": false
+ }
+
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..a659df9
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,26 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ // Configure glob patterns for excluding files and folders.
+ "files.exclude": {
+ "**/.tmp": true,
+ ".git": true,
+ "glide.lock": true,
+ "vendor": true,
+ "debug": true,
+ "bin": true,
+ "tools": true
+ },
+ // Words to add to dictionary for a workspace.
+ "cSpell.words": [
+ "apiv",
+ "iosk",
+ "zhouhui",
+ "ldflags",
+ "socketio",
+ "xdsconfig",
+ "sdkid",
+ "godotenv",
+ "crosssdk",
+ "prjs"
+ ]
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ef2644d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,153 @@
+# Makefile used to build xds-cli commands
+
+# Application Version
+TARGET=xds-cli
+
+
+# Retrieve git tag/commit to set version & sub-version strings
+GIT_DESC := $(shell git describe --always --tags)
+VERSION := $(firstword $(subst -, ,$(GIT_DESC)))
+SUB_VERSION := $(wordlist 2,3,$(subst -, ,$(GIT_DESC)))
+ifeq ($(VERSION), )
+ VERSION := unknown-dev
+endif
+ifeq ($(SUB_VERSION), )
+ SUB_VERSION := $(shell date +'%Y-%m-%d_%H%M%S')
+endif
+
+# Configurable variables for installation (default /opt/AGL/...)
+ifeq ($(origin DESTDIR), undefined)
+ DESTDIR := /opt/AGL/xds/cli
+endif
+
+HOST_GOOS=$(shell go env GOOS)
+HOST_GOARCH=$(shell go env GOARCH)
+ARCH=$(HOST_GOOS)-$(HOST_GOARCH)
+REPOPATH=github.com/iotbzh/$(TARGET)
+
+EXT=
+ifeq ($(HOST_GOOS), windows)
+ EXT=.exe
+endif
+
+mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
+ROOT_SRCDIR := $(patsubst %/,%,$(dir $(mkfile_path)))
+ROOT_GOPRJ := $(abspath $(ROOT_SRCDIR)/../../../..)
+LOCAL_BINDIR := $(ROOT_SRCDIR)/bin
+LOCAL_TOOLSDIR := $(ROOT_SRCDIR)/tools/${HOST_GOOS}
+PACKAGE_DIR := $(ROOT_SRCDIR)/package
+
+export GOPATH := $(shell go env GOPATH):$(ROOT_GOPRJ)
+export PATH := $(PATH):$(LOCAL_TOOLSDIR)
+
+VERBOSE_1 := -v
+VERBOSE_2 := -v -x
+
+# Release or Debug mode
+ifeq ($(filter 1,$(RELEASE) $(REL)),)
+ GO_LDFLAGS=
+ # disable compiler optimizations and inlining
+ GO_GCFLAGS=-N -l
+ BUILD_MODE="Debug mode"
+else
+ # optimized code without debug info
+ GO_LDFLAGS=-s -w
+ GO_GCFLAGS=
+ BUILD_MODE="Release mode"
+endif
+
+
+ifeq ($(SUB_VERSION), )
+ PACKAGE_ZIPFILE := $(TARGET)_$(ARCH)-v$(VERSION).zip
+else
+ PACKAGE_ZIPFILE := $(TARGET)_$(ARCH)-v$(VERSION)_$(SUB_VERSION).zip
+endif
+
+.PHONY: all
+all: vendor build
+
+.PHONY: build
+build:
+ @echo "### Build $(TARGET) (version $(VERSION), subversion $(SUB_VERSION) - $(BUILD_MODE))";
+ @cd $(ROOT_SRCDIR); $(BUILD_ENV_FLAGS) go build $(VERBOSE_$(V)) -i -o $(LOCAL_BINDIR)/$(TARGET)$(EXT) -ldflags "$(GO_LDFLAGS) -X main.AppVersion=$(VERSION) -X main.AppSubVersion=$(SUB_VERSION)" -gcflags "$(GO_GCFLAGS)" .
+
+test: tools/glide
+ go test --race $(shell $(LOCAL_TOOLSDIR)/glide novendor)
+
+vet: tools/glide
+ go vet $(shell $(LOCAL_TOOLSDIR)/glide novendor)
+
+fmt: tools/glide
+ go fmt $(shell $(LOCAL_TOOLSDIR)/glide novendor)
+
+.PHONY: clean
+clean:
+ rm -rf $(LOCAL_BINDIR)/* debug $(ROOT_GOPRJ)/pkg/*/$(REPOPATH) $(PACKAGE_DIR)
+
+.PHONY: distclean
+distclean: clean
+ rm -rf $(LOCAL_BINDIR) $(ROOT_SRCDIR)/tools glide.lock vendor
+
+
+.PHONY: scripts
+scripts:
+ @mkdir -p $(LOCAL_BINDIR) && cp -rf scripts/*.sh scripts/xds-utils $(LOCAL_BINDIR)
+
+.PHONY: release
+release:
+ RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile clean build
+
+package: clean vendor build
+ @mkdir -p $(PACKAGE_DIR)/$(TARGET)
+ @cp -a $(LOCAL_BINDIR)/*cli$(EXT) $(PACKAGE_DIR)/$(TARGET)
+ @cp -r $(ROOT_SRCDIR)/conf.d $(ROOT_SRCDIR)/scripts $(PACKAGE_DIR)/$(TARGET)
+ cd $(PACKAGE_DIR) && zip -r $(ROOT_SRCDIR)/$(PACKAGE_ZIPFILE) ./$(TARGET)
+
+.PHONY: package-all
+package-all:
+ @echo "# Build linux amd64..."
+ GOOS=linux GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+ @echo "# Build windows amd64..."
+ GOOS=windows GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+ @echo "# Build darwin amd64..."
+ GOOS=darwin GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+ make -f $(ROOT_SRCDIR)/Makefile clean
+
+.PHONY: install
+install:
+ @test -e $(LOCAL_BINDIR)/$(TARGET)$(EXT) || { echo "Please execute first: make all\n"; exit 1; }
+ export DESTDIR=$(DESTDIR) && $(ROOT_SRCDIR)/scripts/install.sh
+
+.PHONY: uninstall
+uninstall:
+ export DESTDIR=$(DESTDIR) && $(ROOT_SRCDIR)/scripts/install.sh uninstall
+
+vendor: tools/glide glide.yaml
+ $(LOCAL_TOOLSDIR)/glide install --strip-vendor
+
+vendor/debug: vendor
+ (cd vendor/github.com/iotbzh && \
+ rm -rf xds-common && ln -s ../../../../xds-common && \
+ rm -rf xds-agent && ln -s ../../../../xds-agent )
+
+.PHONY: tools/glide
+tools/glide:
+ @test -f $(LOCAL_TOOLSDIR)/glide || { \
+ echo "Downloading glide"; \
+ mkdir -p $(LOCAL_TOOLSDIR); \
+ curl --silent -L https://glide.sh/get | GOBIN=$(LOCAL_TOOLSDIR) sh; \
+ }
+
+help:
+ @echo "Main supported rules:"
+ @echo " all (default)"
+ @echo " build"
+ @echo " release"
+ @echo " clean"
+ @echo " package"
+ @echo " install / uninstall"
+ @echo " distclean"
+ @echo ""
+ @echo "Influential make variables:"
+ @echo " V - Build verbosity {0,1,2}."
+ @echo " BUILD_ENV_FLAGS - Environment added to 'go build'."
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9c2b663
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# xds-cli
+
+`xds-cli` is a command line tool used to control / interface X(cross) Development System.
+
+## Documentation
+
+Please find XDS User's Guide online at :
+
+[http://docs.automotivelinux.org/docs/devguides/en/dev/#xcross-development-system-user's-guide](http://docs.automotivelinux.org/docs/devguides/en/dev/#xcross-development-system-user's-guide)
+
+and `xds-cli` advanced documentation at :
+
+[http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-2/4_xds-cli.html](http://docs.automotivelinux.org/docs/devguides/en/dev/reference/xds/part-2/4_xds-cli.html)
diff --git a/cmd-exec.go b/cmd-exec.go
new file mode 100644
index 0000000..612851f
--- /dev/null
+++ b/cmd-exec.go
@@ -0,0 +1,166 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/iotbzh/xds-agent/lib/apiv1"
+ common "github.com/iotbzh/xds-common/golib"
+ "github.com/joho/godotenv"
+ "github.com/urfave/cli"
+)
+
+func initCmdExec(cmdDef *[]cli.Command) {
+ *cmdDef = append(*cmdDef, cli.Command{
+ Name: "exec",
+ Usage: "execute a command in XDS",
+ Action: exec,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "id",
+ EnvVar: "XDS_PROJECT_ID",
+ Usage: "project ID you want to build (mandatory variable)",
+ },
+ cli.StringFlag{
+ Name: "rpath",
+ EnvVar: "XDS_RPATH",
+ Usage: "relative path into project",
+ },
+ cli.StringFlag{
+ Name: "sdkid",
+ EnvVar: "XDS_SDK_ID",
+ Usage: "Cross Sdk ID to use to build project",
+ },
+ },
+ })
+}
+
+func exec(ctx *cli.Context) error {
+ prjID := ctx.String("id")
+ confFile := ctx.String("config")
+ rPath := ctx.String("rPath")
+ sdkid := ctx.String("sdkid")
+
+ // Check mandatory args
+ if prjID == "" {
+ return cli.NewExitError("project id must be set (see --id option)", 1)
+ }
+
+ // Load config file if requested
+ envMap := make(map[string]string)
+ if confFile != "" {
+ if !common.Exists(confFile) {
+ exitError(1, "Error env config file not found")
+ }
+ // Load config file variables that will overwrite env variables
+ err := godotenv.Overload(confFile)
+ if err != nil {
+ exitError(1, "Error loading env config file "+confFile)
+ }
+ envMap, err = godotenv.Read(confFile)
+ if err != nil {
+ exitError(1, "Error reading env config file "+confFile)
+ }
+ }
+
+ argsCommand := make([]string, len(ctx.Args()))
+ copy(argsCommand, ctx.Args())
+ Log.Infof("Execute: /exec %v", argsCommand)
+
+ // Log useful info for debugging
+ ver := apiv1.XDSVersion{}
+ XdsVersionGet(&ver)
+ Log.Infof("XDS version: %v", ver)
+
+ // Process Socket IO events
+ type exitResult struct {
+ error error
+ code int
+ }
+ exitChan := make(chan exitResult, 1)
+
+ IOsk.On("disconnection", func(err error) {
+ exitChan <- exitResult{err, 2}
+ })
+
+ outFunc := func(timestamp, stdout, stderr string) {
+ tm := ""
+ if ctx.Bool("WithTimestamp") {
+ tm = timestamp + "| "
+ }
+ if stdout != "" {
+ fmt.Printf("%s%s", tm, stdout)
+ }
+ if stderr != "" {
+ fmt.Fprintf(os.Stderr, "%s%s", tm, stderr)
+ }
+ }
+
+ IOsk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) {
+ outFunc(ev.Timestamp, ev.Stdout, ev.Stderr)
+ })
+
+ IOsk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) {
+ exitChan <- exitResult{ev.Error, ev.Code}
+ })
+
+ // Retrieve the project definition
+ prj := apiv1.ProjectConfig{}
+ if err := HTTPCli.Get("/projects/"+prjID, &prj); err != nil {
+ return cli.NewExitError(err, 1)
+ }
+
+ // Auto setup rPath if needed
+ if rPath == "" {
+ cwd, err := os.Getwd()
+ if err == nil {
+ fldRp := prj.ClientPath
+ if !strings.HasPrefix(fldRp, "/") {
+ fldRp = "/" + fldRp
+ }
+ Log.Debugf("Try to auto-setup rPath: cwd=%s ; ClientPath=%s", cwd, fldRp)
+ if sp := strings.SplitAfter(cwd, fldRp); len(sp) == 2 {
+ rPath = strings.Trim(sp[1], "/")
+ Log.Debugf("Auto-setup rPath to: '%s'", rPath)
+ }
+ }
+ }
+
+ // Build env
+ Log.Debugf("Command env: %v", envMap)
+ env := []string{}
+ for k, v := range envMap {
+ env = append(env, k+"="+v)
+ }
+
+ // Send build command
+ args := apiv1.ExecArgs{
+ ID: prjID,
+ SdkID: sdkid,
+ Cmd: strings.Trim(argsCommand[0], " "),
+ Args: argsCommand[1:],
+ Env: env,
+ RPath: rPath,
+ CmdTimeout: 60,
+ }
+
+ LogPost("POST /exec %v", args)
+ if err := HTTPCli.Post("/exec", args, nil); err != nil {
+ return cli.NewExitError(err.Error(), 1)
+ }
+
+ // Wait exit
+ select {
+ case res := <-exitChan:
+ errStr := ""
+ if res.code == 0 {
+ Log.Debugln("Exit successfully")
+ }
+ if res.error != nil {
+ Log.Debugln("Exit with ERROR: ", res.error.Error())
+ errStr = res.error.Error()
+ }
+ return cli.NewExitError(errStr, res.code)
+ }
+}
diff --git a/cmd-misc.go b/cmd-misc.go
new file mode 100644
index 0000000..b4b579d
--- /dev/null
+++ b/cmd-misc.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/iotbzh/xds-agent/lib/apiv1"
+ "github.com/urfave/cli"
+)
+
+func initCmdMisc(cmdDef *[]cli.Command) {
+ *cmdDef = append(*cmdDef, cli.Command{
+ Name: "misc",
+ HideHelp: true,
+ Usage: "miscellaneous commands group",
+ Subcommands: []cli.Command{
+ {
+ Name: "version",
+ Aliases: []string{"v"},
+ Usage: "Get version of XDS agent and XDS server",
+ Action: xdsVersion,
+ Flags: []cli.Flag{
+ cli.BoolFlag{
+ Name: "verbose, v",
+ Usage: "display verbose output",
+ },
+ },
+ },
+ },
+ })
+}
+
+func xdsVersion(ctx *cli.Context) error {
+ verbose := ctx.Bool("verbose")
+
+ // Get version
+ ver := apiv1.XDSVersion{}
+ if err := XdsVersionGet(&ver); err != nil {
+ return cli.NewExitError(err.Error(), 1)
+ }
+
+ writer := NewTableWriter()
+ fmt.Fprintln(writer, "Agent ID:\t", ver.Client.ID)
+ v := ver.Client.Version
+ if verbose {
+ v += " (" + ver.Client.VersionGitTag + ")"
+ }
+ fmt.Fprintln(writer, " Version:\t", v)
+ if verbose {
+ fmt.Fprintln(writer, " API Version:\t", ver.Client.APIVersion)
+ }
+
+ for _, svr := range ver.Server {
+ fmt.Fprintln(writer, "Server ID:\t", svr.ID)
+ v = svr.Version
+ if verbose {
+ v += " (" + svr.VersionGitTag + ")"
+ }
+ fmt.Fprintln(writer, " Version:\t", v)
+ if verbose {
+ fmt.Fprintln(writer, " API Version:\t", svr.APIVersion)
+ }
+ }
+ writer.Flush()
+
+ return nil
+}
diff --git a/cmd-projects.go b/cmd-projects.go
new file mode 100644
index 0000000..9a113db
--- /dev/null
+++ b/cmd-projects.go
@@ -0,0 +1,209 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/iotbzh/xds-agent/lib/apiv1"
+ "github.com/urfave/cli"
+)
+
+func initCmdProjects(cmdDef *[]cli.Command) {
+ *cmdDef = append(*cmdDef, cli.Command{
+ Name: "projects",
+ Aliases: []string{"prj"},
+ HideHelp: true,
+ Usage: "project commands group",
+ Subcommands: []cli.Command{
+ {
+ Name: "add",
+ Aliases: []string{"a"},
+ Usage: "Add a new project",
+ Action: projectsAdd,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "label",
+ Usage: "project label (free form string)",
+ },
+ cli.StringFlag{
+ Name: "path",
+ Usage: "project local path",
+ },
+ cli.StringFlag{
+ Name: "server-path",
+ Usage: "project server path (only used with pathmap type)",
+ },
+ cli.StringFlag{
+ Name: "type",
+ Usage: "project type (pathmap|pm, cloudsync|sc)",
+ },
+ },
+ },
+ {
+ Name: "get",
+ Usage: "Get a property of a project",
+ Action: projectsGet,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "id",
+ Usage: "project id",
+ },
+ },
+ },
+ {
+ Name: "list",
+ Aliases: []string{"ls"},
+ Usage: "List existing projects",
+ Action: projectsList,
+ Flags: []cli.Flag{
+ cli.BoolFlag{
+ Name: "verbose, v",
+ Usage: "display verbose output",
+ },
+ },
+ },
+ {
+ Name: "remove",
+ Aliases: []string{"rm"},
+ Usage: "Remove an existing project",
+ Action: projectsRemove,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "id",
+ Usage: "project id",
+ },
+ },
+ },
+ {
+ Name: "sync",
+ Aliases: []string{},
+ Usage: "Force synchronization of project sources",
+ Action: projectsSync,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "id",
+ Usage: "project id",
+ },
+ },
+ },
+ },
+ })
+}
+
+func projectsList(ctx *cli.Context) error {
+ // Get projects list
+ prjs := []apiv1.ProjectConfig{}
+ if err := ProjectsListGet(&prjs); err != nil {
+ return cli.NewExitError(err.Error(), 1)
+ }
+ _displayProjects(prjs, ctx.Bool("verbose"))
+ return nil
+}
+
+func projectsGet(ctx *cli.Context) error {
+ id := GetID(ctx)
+ if id == "" {
+ return cli.NewExitError("id parameter or option must be set", 1)
+ }
+ prjs := make([]apiv1.ProjectConfig, 1)
+ if err := HTTPCli.Get("/projects/"+id, &prjs[0]); err != nil {
+ return cli.NewExitError(err, 1)
+ }
+ _displayProjects(prjs, true)
+ return nil
+}
+
+func _displayProjects(prjs []apiv1.ProjectConfig, verbose bool) {
+ // Display result
+ first := true
+ writer := NewTableWriter()
+ for _, folder := range prjs {
+ if verbose {
+ if !first {
+ fmt.Fprintln(writer)
+ }
+ fmt.Fprintln(writer, "ID:\t", folder.ID)
+ fmt.Fprintln(writer, "Label:\t", folder.Label)
+ fmt.Fprintln(writer, "Path type:\t", folder.Type)
+ fmt.Fprintln(writer, "Local Path:\t", folder.ClientPath)
+ if folder.Type != apiv1.TypeCloudSync {
+ fmt.Fprintln(writer, "Server Path:\t", folder.ServerPath)
+ }
+ fmt.Fprintln(writer, "Status:\t", folder.Status)
+ fmt.Fprintln(writer, "Is in Sync:\t", folder.IsInSync)
+ ds := folder.DefaultSdk
+ if ds == "" {
+ ds = "-"
+ }
+ fmt.Fprintln(writer, "Default Sdk:\t", ds)
+
+ } else {
+ if first {
+ fmt.Fprintln(writer, "ID\t Label\t LocalPath")
+ }
+ fmt.Fprintln(writer, folder.ID, "\t", folder.Label, "\t", folder.ClientPath)
+ }
+ first = false
+ }
+ writer.Flush()
+}
+
+func projectsAdd(ctx *cli.Context) error {
+
+ // Decode project type
+ var ptype apiv1.ProjectType
+ switch strings.ToLower(ctx.String("type")) {
+ case "pathmap", "pm":
+ ptype = apiv1.TypePathMap
+ case "cloudsync", "cs":
+ ptype = apiv1.TypeCloudSync
+ default:
+ return cli.NewExitError("Unknown project type", 1)
+ }
+
+ prj := apiv1.ProjectConfig{
+ ServerID: XdsServerIDGet(),
+ Label: ctx.String("label"),
+ Type: ptype,
+ ClientPath: ctx.String("path"),
+ ServerPath: ctx.String("server-path"),
+ }
+
+ Log.Infof("POST /project %v", prj)
+ newPrj := apiv1.ProjectConfig{}
+ err := HTTPCli.Post("/projects", prj, &newPrj)
+ if err != nil {
+ return cli.NewExitError(err, 1)
+ }
+
+ fmt.Printf("New project '%s' (id %v) successfully created.\n", newPrj.Label, newPrj.ID)
+
+ return nil
+}
+
+func projectsRemove(ctx *cli.Context) error {
+ var res apiv1.ProjectConfig
+ id := GetID(ctx)
+ if id == "" {
+ return cli.NewExitError("id parameter or option must be set", 1)
+ }
+
+ if err := HTTPCli.Delete("/projects/"+id, &res); err != nil {
+ return cli.NewExitError(err, 1)
+ }
+
+ fmt.Println("Project ID " + res.ID + " successfully deleted.")
+ return nil
+}
+
+func projectsSync(ctx *cli.Context) error {
+ id := GetID(ctx)
+ if id == "" {
+ return cli.NewExitError("id parameter or option must be set", 1)
+ }
+ if err := HTTPCli.Post("/projects/sync/"+id, "", nil); err != nil {
+ return cli.NewExitError(err, 1)
+ }
+ fmt.Println("Sync successfully resquested.")
+ return nil
+}
diff --git a/cmd-sdks.go b/cmd-sdks.go
new file mode 100644
index 0000000..8568b3a
--- /dev/null
+++ b/cmd-sdks.go
@@ -0,0 +1,136 @@
+package main
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/iotbzh/xds-agent/lib/apiv1"
+ "github.com/urfave/cli"
+)
+
+func initCmdSdks(cmdDef *[]cli.Command) {
+ *cmdDef = append(*cmdDef, cli.Command{
+ Name: "sdks",
+ Aliases: []string{"sdk"},
+ HideHelp: true,
+ Usage: "SDKs commands group",
+ Subcommands: []cli.Command{
+ {
+ Name: "add",
+ Aliases: []string{"a"},
+ Usage: "Add a new SDK",
+ Action: sdksAdd,
+ },
+ {
+ Name: "get",
+ Usage: "Get a property of a SDK",
+ Action: sdksGet,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "id",
+ Usage: "sdk id",
+ },
+ },
+ },
+ {
+ Name: "list",
+ Aliases: []string{"ls"},
+ Usage: "List installed SDKs",
+ Action: sdksList,
+ Flags: []cli.Flag{
+ cli.BoolFlag{
+ Name: "verbose, v",
+ Usage: "display verbose output",
+ },
+ },
+ },
+ {
+ Name: "remove",
+ Aliases: []string{"rm"},
+ Usage: "Remove an existing SDK",
+ Action: sdksRemove,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "id",
+ Usage: "sdk id",
+ },
+ },
+ },
+ },
+ })
+}
+
+func sdksList(ctx *cli.Context) error {
+ // Get SDKs list
+ sdks := []apiv1.SDK{}
+ if err := sdksListGet(&sdks); err != nil {
+ return cli.NewExitError(err.Error(), 1)
+ }
+ _displaySdks(sdks, ctx.Bool("verbose"))
+ return nil
+}
+
+func sdksGet(ctx *cli.Context) error {
+ id := GetID(ctx)
+ if id == "" {
+ return cli.NewExitError("id parameter or option must be set", 1)
+ }
+ sdks := apiv1.SDK{}
+ url := "server/" + strconv.Itoa(XdsServerIndexGet()) + "/sdks/" + id
+ if err := HTTPCli.Get(url, &sdks); err != nil {
+ return cli.NewExitError(err.Error(), 1)
+ }
+ _displaySdks([]apiv1.SDK{sdks}, true)
+ return nil
+}
+
+func _displaySdks(sdks []apiv1.SDK, verbose bool) {
+ // Display result
+ first := true
+ writer := NewTableWriter()
+ for _, s := range sdks {
+ if verbose {
+ if !first {
+ fmt.Fprintln(writer)
+ }
+ fmt.Fprintln(writer, "ID\t"+s.ID)
+ fmt.Fprintln(writer, "Name\t"+s.Name)
+ fmt.Fprintln(writer, "Profile\t"+s.Profile)
+ fmt.Fprintln(writer, "Arch\t"+s.Arch)
+ fmt.Fprintln(writer, "Version\t"+s.Version)
+ fmt.Fprintln(writer, "Path\t"+s.Path)
+
+ } else {
+ if first {
+ fmt.Fprintf(writer, "List of installed SDKs: \n")
+ fmt.Fprintf(writer, " ID\tNAME\n")
+ }
+ fmt.Fprintf(writer, " %s\t%s\n", s.ID, s.Name)
+ }
+ first = false
+ }
+ writer.Flush()
+}
+
+func sdksListGet(sdks *[]apiv1.SDK) error {
+ url := "server/" + strconv.Itoa(XdsServerIndexGet()) + "/sdks"
+ if err := HTTPCli.Get(url, &sdks); err != nil {
+ return err
+ }
+ Log.Debugf("Result of %s: %v", url, sdks)
+
+ return nil
+}
+
+func sdksAdd(ctx *cli.Context) error {
+ return fmt.Errorf("not supported yet")
+}
+
+func sdksRemove(ctx *cli.Context) error {
+ id := GetID(ctx)
+ if id == "" {
+ return cli.NewExitError("id parameter or option must be set", 1)
+ }
+
+ return fmt.Errorf("not supported yet")
+}
diff --git a/glide.yaml b/glide.yaml
new file mode 100644
index 0000000..5822fdc
--- /dev/null
+++ b/glide.yaml
@@ -0,0 +1,25 @@
+package: github.com/iotbzh/xds-cli
+license: Apache-2.0
+owners:
+- name: Sebastien Douheret
+ email: sebastien@iot.bzh
+import:
+- package: github.com/urfave/cli
+ version: ^1.19.1
+- package: github.com/Sirupsen/logrus
+ version: ^0.11.5
+- package: github.com/zhouhui8915/engine.io-go
+- package: github.com/sebd71/go-socket.io-client
+ version: 46defcb47f
+- package: github.com/iotbzh/xds-agent
+ version: ^1.0.0-rc.1
+ subpackages:
+ - agent
+- package: github.com/iotbzh/xds-common
+ version: ^0.1.0
+ subpackages:
+ - golib/common
+- package: github.com/joho/godotenv
+ version: ^1.1.0
+ subpackages:
+ - cmd/godotenv
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..c8d2095
--- /dev/null
+++ b/main.go
@@ -0,0 +1,263 @@
+// xds-cli: command line tool used to control / interface X(cross) Development System.
+package main
+
+import (
+ "fmt"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/Sirupsen/logrus"
+ common "github.com/iotbzh/xds-common/golib"
+ socketio_client "github.com/sebd71/go-socket.io-client"
+ "github.com/urfave/cli"
+)
+
+var appAuthors = []cli.Author{
+ cli.Author{Name: "Sebastien Douheret", Email: "sebastien@iot.bzh"},
+}
+
+// AppName name of this application
+var AppName = "xds-cli"
+
+// AppNativeName native command name that this application can overload
+var AppNativeName = "cli"
+
+// AppVersion Version of this application
+// (set by Makefile)
+var AppVersion = "?.?.?"
+
+// AppSubVersion is the git tag id added to version string
+// Should be set by compilation -ldflags "-X main.AppSubVersion=xxx"
+// (set by Makefile)
+var AppSubVersion = "unknown-dev"
+
+// Application details
+const (
+ appCopyright = "Apache-2.0"
+ defaultLogLevel = "error"
+)
+
+// Log Global variable that hold logger
+var Log = logrus.New()
+
+// HTTPCli Global variable that hold HTTP Client
+var HTTPCli *common.HTTPClient
+
+// IOsk Global variable that hold SocketIo client
+var IOsk *socketio_client.Client
+
+// exitError exists this program with the specified error
+func exitError(code int, f string, a ...interface{}) {
+ err := fmt.Sprintf(f, a...)
+ fmt.Fprintf(os.Stderr, err+"\n")
+ os.Exit(code)
+}
+
+// main
+func main() {
+
+ // Allow to set app name from cli (useful for debugging)
+ if AppName == "" {
+ AppName = os.Getenv("XDS_APPNAME")
+ }
+ if AppName == "" {
+ panic("Invalid setup, AppName not define !")
+ }
+ if AppNativeName == "" {
+ AppNativeName = AppName[4:]
+ }
+ appUsage := fmt.Sprintf("command line tool for X(cross) Development System.")
+ appDescription := fmt.Sprintf("%s utility for X(cross) Development System\n", AppName)
+ /* SEB UPDATE DOC
+ appDescription += `
+ xds-cli configuration is driven either by environment variables or by command line
+ options or using a config file knowning that the following priority order is used:
+ 1. use option value (for example use project ID set by --id option),
+ 2. else use variable 'XDS_xxx' (for example 'XDS_PROJECT_ID' variable) when a
+ config file is specified with '--config|-c' option,
+ 3. else use 'XDS_xxx' (for example 'XDS_PROJECT_ID') environment variable.
+ `
+ */
+ // Create a new App instance
+ app := cli.NewApp()
+ app.Name = AppName
+ app.Usage = appUsage
+ app.Version = AppVersion + " (" + AppSubVersion + ")"
+ app.Authors = appAuthors
+ app.Copyright = appCopyright
+ app.Metadata = make(map[string]interface{})
+ app.Metadata["version"] = AppVersion
+ app.Metadata["git-tag"] = AppSubVersion
+ app.Metadata["logger"] = Log
+
+ // Create env vars help
+ dynDesc := "\nENVIRONMENT VARIABLES:"
+ for _, f := range app.Flags {
+ var env, usage string
+ switch f.(type) {
+ case cli.StringFlag:
+ fs := f.(cli.StringFlag)
+ env = fs.EnvVar
+ usage = fs.Usage
+ case cli.BoolFlag:
+ fb := f.(cli.BoolFlag)
+ env = fb.EnvVar
+ usage = fb.Usage
+ default:
+ exitError(1, "Un-implemented option type")
+ }
+ if env != "" {
+ dynDesc += fmt.Sprintf("\n %s \t\t %s", env, usage)
+ }
+ }
+ app.Description = appDescription + dynDesc
+
+ // Declare global flags
+ app.Flags = []cli.Flag{
+ cli.StringFlag{
+ Name: "config, c",
+ EnvVar: "XDS_CONFIG",
+ Usage: "env config file to source on startup",
+ },
+ cli.StringFlag{
+ Name: "log, l",
+ EnvVar: "XDS_LOGLEVEL",
+ Usage: "logging level (supported levels: panic, fatal, error, warn, info, debug)",
+ Value: defaultLogLevel,
+ },
+ cli.StringFlag{
+ Name: "url",
+ EnvVar: "XDS_SERVER_URL",
+ Value: "localhost:8000",
+ Usage: "remote XDS server url",
+ },
+ cli.BoolFlag{
+ Name: "timestamp, ts",
+ EnvVar: "XDS_TIMESTAMP",
+ Usage: "prefix output with timestamp",
+ },
+ }
+
+ // Declare commands
+ app.Commands = []cli.Command{}
+
+ initCmdProjects(&app.Commands)
+ initCmdSdks(&app.Commands)
+ initCmdExec(&app.Commands)
+ initCmdMisc(&app.Commands)
+
+ sort.Sort(cli.FlagsByName(app.Flags))
+ sort.Sort(cli.CommandsByName(app.Commands))
+
+ app.Before = func(ctx *cli.Context) error {
+ var err error
+ loglevel := ctx.String("log")
+ // Set logger level and formatter
+ if Log.Level, err = logrus.ParseLevel(loglevel); err != nil {
+ msg := fmt.Sprintf("Invalid log level : \"%v\"\n", loglevel)
+ return cli.NewExitError(msg, 1)
+ }
+ Log.Formatter = &logrus.TextFormatter{}
+
+ Log.Infof("%s version: %s", AppName, app.Version)
+ // SEB Add again Log.Debugf("Environment: %v", os.Environ())
+
+ if err = XdsConnInit(ctx); err != nil {
+ // Directly call HandleExitCoder to avoid to print help (ShowAppHelp)
+ // Note that this function wil never return and program will exit
+ cli.HandleExitCoder(err)
+ }
+
+ return nil
+ }
+
+ // Close HTTP client and WS connection on exit
+ defer func() {
+ XdsConnClose()
+ }()
+
+ app.Run(os.Args)
+}
+
+// XdsConnInit Initialized HTTP and WebSocket connection to XDS agent
+func XdsConnInit(ctx *cli.Context) error {
+ var err error
+
+ // Define HTTP and WS url
+ baseURL := ctx.String("url")
+ if !strings.HasPrefix(ctx.String("url"), "http://") {
+ baseURL = "http://" + ctx.String("url")
+ }
+
+ // Create HTTP client
+ Log.Debugln("Connect HTTP client on ", baseURL)
+ conf := common.HTTPClientConfig{
+ URLPrefix: "/api/v1",
+ HeaderClientKeyName: "Xds-Agent-Sid",
+ CsrfDisable: true,
+ LogOut: Log.Out,
+ LogPrefix: "XDSAGENT: ",
+ LogLevel: common.HTTPLogLevelWarning,
+ }
+
+ HTTPCli, err = common.HTTPNewClient(baseURL, conf)
+ if err != nil {
+ errmsg := err.Error()
+ if m, err := regexp.MatchString("Get http.?://", errmsg); m && err == nil {
+ i := strings.LastIndex(errmsg, ":")
+ errmsg = "Cannot connection to " + baseURL + errmsg[i:]
+ }
+ return cli.NewExitError(errmsg, 1)
+ }
+ HTTPCli.SetLogLevel(ctx.String("loglevel"))
+
+ // Create io Websocket client
+ Log.Debugln("Connecting IO.socket client on ", baseURL)
+
+ opts := &socketio_client.Options{
+ Transport: "websocket",
+ Header: make(map[string][]string),
+ }
+ opts.Header["XDS-AGENT-SID"] = []string{HTTPCli.GetClientID()}
+
+ IOsk, err = socketio_client.NewClient(baseURL, opts)
+ if err != nil {
+ return cli.NewExitError("IO.socket connection error: "+err.Error(), 1)
+ }
+
+ IOsk.On("error", func(err error) {
+ fmt.Println("ERROR Websocket: ", err.Error())
+ })
+
+ ctx.App.Metadata["httpCli"] = HTTPCli
+ ctx.App.Metadata["ioskCli"] = IOsk
+
+ return nil
+}
+
+// XdsConnClose Terminate connection to XDS agent
+func XdsConnClose() {
+ Log.Debugf("Closing HTTP client session...")
+ /* TODO
+ if httpCli, ok := app.Metadata["httpCli"]; ok {
+ c := httpCli.(*common.HTTPClient)
+ }
+ */
+
+ Log.Debugf("Closing WebSocket connection...")
+ /*
+ if ioskCli, ok := app.Metadata["ioskCli"]; ok {
+ c := ioskCli.(*socketio_client.Client)
+ }
+ */
+}
+
+// NewTableWriter Create a writer that inserts padding around tab-delimited
+func NewTableWriter() *tabwriter.Writer {
+ writer := new(tabwriter.Writer)
+ writer.Init(os.Stdout, 0, 8, 0, '\t', 0)
+ return writer
+}
diff --git a/utils.go b/utils.go
new file mode 100644
index 0000000..6a05ef3
--- /dev/null
+++ b/utils.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "encoding/json"
+
+ "github.com/iotbzh/xds-agent/lib/apiv1"
+ "github.com/urfave/cli"
+)
+
+var cacheXdsVersion *apiv1.XDSVersion
+
+// XdsVersionGet Get version of XDS agent & server
+func XdsVersionGet(ver *apiv1.XDSVersion) error {
+ // Use cached data
+ if cacheXdsVersion != nil {
+ ver = cacheXdsVersion
+ return nil
+ }
+
+ dataVer := apiv1.XDSVersion{}
+ if err := HTTPCli.Get("/version", &dataVer); err != nil {
+ return err
+ }
+
+ cacheXdsVersion = &dataVer
+ *ver = dataVer
+ return nil
+}
+
+// XdsServerIDGet returns the XDS Server ID
+func XdsServerIDGet() string {
+ ver := apiv1.XDSVersion{}
+ if err := XdsVersionGet(&ver); err != nil {
+ return ""
+ }
+ if len(ver.Server) < 1 {
+ return ""
+ }
+ return ver.Server[XdsServerIndexGet()].ID
+}
+
+// XdsServerIndexGet returns the index number of XDS Server
+func XdsServerIndexGet() int {
+ // FIXME support multiple server
+ return 0
+}
+
+// ProjectsListGet Get the list of existing projects
+func ProjectsListGet(prjs *[]apiv1.ProjectConfig) error {
+ var data []byte
+ if err := HTTPCli.HTTPGet("/projects", &data); err != nil {
+ return err
+ }
+ Log.Debugf("Result of /projects: %v", string(data[:]))
+
+ return json.Unmarshal(data, &prjs)
+}
+
+// LogPost Helper to log a POST request
+func LogPost(format string, data interface{}) {
+ b, _ := json.Marshal(data)
+ Log.Infof(format, string(b))
+}
+
+// GetID Return a string ID set with --id option or as simple parameter
+func GetID(ctx *cli.Context) string {
+ id := ctx.String("id")
+ idArgs := ctx.Args().First()
+ if id == "" && idArgs != "" {
+ id = idArgs
+ }
+ return id
+}