mirror of https://github.com/dapr/cli.git
Setup for addition of dashboard self-hosted command (#417)
* added function to install dashboard binaries * added to waitgroup * updated installBinary method to support non-dapr repositories * adding dashboard binaries * binary install working for daprd and dashboard on linux * added dashboard command for standalone * updated dashboard documentation * changed linux home directory to be outside of root * fixing version issue * moved location of extracted web folder * fixing path issues on linux * changed dashboard to take latest version * updated documentation for dashboard standalone * fixed issue where binary name was unknown in non-default path dapr install * added user error message for dashboard not found error * fixing linting issues * adding dashboard compatibility changes * mark dashboard flag as required * syncing compatibility branch and master * fixing uninstall error * removing unnecessary check * removing unused constants * changed standalone untar method openfile mode * added missing error message * removed windows binary check in untar method * updating unzip method to support multiple files in archive * adding sanitizeExtractPath method and limiting archive copy bytes * changed max file size to 100MB * removing max file size limit * removing debug statement * removing tabs in cmd/dashboard.go * fixing whitespace issues Co-authored-by: Shalabh Mohan Shrivastava <shalabhs@microsoft.com>
This commit is contained in:
parent
06d64bf7ac
commit
12b0651c94
162
cmd/dashboard.go
162
cmd/dashboard.go
|
@ -49,90 +49,92 @@ var DashboardCmd = &cobra.Command{
|
|||
localPort = port
|
||||
}
|
||||
|
||||
config, client, err := kubernetes.GetKubeConfigClient()
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to initialize kubernetes client: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// search for dashboard service namespace in order:
|
||||
// user-supplied namespace, dapr-system, default
|
||||
namespaces := []string{dashboardNamespace}
|
||||
if dashboardNamespace != daprSystemNamespace {
|
||||
namespaces = append(namespaces, daprSystemNamespace)
|
||||
}
|
||||
if dashboardNamespace != defaultNamespace {
|
||||
namespaces = append(namespaces, defaultNamespace)
|
||||
}
|
||||
|
||||
foundNamespace := ""
|
||||
for _, namespace := range namespaces {
|
||||
ok, _ := kubernetes.CheckPodExists(client, namespace, nil, dashboardSvc)
|
||||
if ok {
|
||||
foundNamespace = namespace
|
||||
break
|
||||
if kubernetesMode {
|
||||
config, client, err := kubernetes.GetKubeConfigClient()
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to initialize kubernetes client: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// if the service is not found, try to search all pods
|
||||
if foundNamespace == "" {
|
||||
ok, nspace := kubernetes.CheckPodExists(client, "", nil, dashboardSvc)
|
||||
|
||||
// if the service is found, tell the user to try with the found namespace
|
||||
// if the service is still not found, throw an error
|
||||
if ok {
|
||||
print.InfoStatusEvent(os.Stdout, "Dapr dashboard found in namespace: %s. Run dapr dashboard -k -n %s to use this namespace.", nspace, nspace)
|
||||
|
||||
} else {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to find Dapr dashboard in cluster. Check status of dapr dashboard in the cluster.")
|
||||
// search for dashboard service namespace in order:
|
||||
// user-supplied namespace, dapr-system, default
|
||||
namespaces := []string{dashboardNamespace}
|
||||
if dashboardNamespace != daprSystemNamespace {
|
||||
namespaces = append(namespaces, daprSystemNamespace)
|
||||
}
|
||||
os.Exit(1)
|
||||
if dashboardNamespace != defaultNamespace {
|
||||
namespaces = append(namespaces, defaultNamespace)
|
||||
}
|
||||
|
||||
foundNamespace := ""
|
||||
for _, namespace := range namespaces {
|
||||
ok, _ := kubernetes.CheckPodExists(client, namespace, nil, dashboardSvc)
|
||||
if ok {
|
||||
foundNamespace = namespace
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if the service is not found, try to search all pods
|
||||
if foundNamespace == "" {
|
||||
ok, nspace := kubernetes.CheckPodExists(client, "", nil, dashboardSvc)
|
||||
|
||||
// if the service is found, tell the user to try with the found namespace
|
||||
// if the service is still not found, throw an error
|
||||
if ok {
|
||||
print.InfoStatusEvent(os.Stdout, "Dapr dashboard found in namespace: %s. Run dapr dashboard -k -n %s to use this namespace.", nspace, nspace)
|
||||
|
||||
} else {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to find Dapr dashboard in cluster. Check status of dapr dashboard in the cluster.")
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// manage termination of port forwarding connection on interrupt
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt)
|
||||
defer signal.Stop(signals)
|
||||
|
||||
portForward, err := kubernetes.NewPortForward(
|
||||
config,
|
||||
foundNamespace,
|
||||
dashboardSvc,
|
||||
defaultHost,
|
||||
localPort,
|
||||
remotePort,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// initialize port forwarding
|
||||
if err = portForward.Init(); err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Error in port forwarding: %s\nCheck for `dapr dashboard` running in other terminal sessions, or use the `--port` flag to use a different port.\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// block until interrupt signal is received
|
||||
go func() {
|
||||
<-signals
|
||||
portForward.Stop()
|
||||
}()
|
||||
|
||||
// url for dashboard after port forwarding
|
||||
var webURL string = fmt.Sprintf("http://%s:%d", defaultHost, localPort)
|
||||
|
||||
print.InfoStatusEvent(os.Stdout, fmt.Sprintf("Dapr dashboard found in namespace:\t%s", foundNamespace))
|
||||
print.InfoStatusEvent(os.Stdout, fmt.Sprintf("Dapr dashboard available at:\t%s\n", webURL))
|
||||
|
||||
err = browser.OpenURL(webURL)
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to start Dapr dashboard in browser automatically")
|
||||
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Visit %s in your browser to view the dashboard", webURL))
|
||||
}
|
||||
|
||||
<-portForward.GetStop()
|
||||
}
|
||||
|
||||
// manage termination of port forwarding connection on interrupt
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt)
|
||||
defer signal.Stop(signals)
|
||||
|
||||
portForward, err := kubernetes.NewPortForward(
|
||||
config,
|
||||
foundNamespace,
|
||||
dashboardSvc,
|
||||
defaultHost,
|
||||
localPort,
|
||||
remotePort,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// initialize port forwarding
|
||||
if err = portForward.Init(); err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Error in port forwarding: %s\nCheck for `dapr dashboard` running in other terminal sessions, or use the `--port` flag to use a different port.\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// block until interrupt signal is received
|
||||
go func() {
|
||||
<-signals
|
||||
portForward.Stop()
|
||||
}()
|
||||
|
||||
// url for dashboard after port forwarding
|
||||
var webURL string = fmt.Sprintf("http://%s:%d", defaultHost, localPort)
|
||||
|
||||
print.InfoStatusEvent(os.Stdout, fmt.Sprintf("Dapr dashboard found in namespace:\t%s", foundNamespace))
|
||||
print.InfoStatusEvent(os.Stdout, fmt.Sprintf("Dapr dashboard available at:\t%s\n", webURL))
|
||||
|
||||
err = browser.OpenURL(webURL)
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stdout, "Failed to start Dapr dashboard in browser automatically")
|
||||
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Visit %s in your browser to view the dashboard", webURL))
|
||||
}
|
||||
|
||||
<-portForward.GetStop()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -148,11 +148,11 @@ func Init(runtimeVersion string, dockerNetwork string, redisHost string, slimMod
|
|||
}
|
||||
|
||||
// Initialize daprd binary
|
||||
go installBinary(&wg, errorChan, daprBinDir, runtimeVersion, daprRuntimeFilePrefix, dockerNetwork)
|
||||
go installBinary(&wg, errorChan, daprBinDir, runtimeVersion, daprRuntimeFilePrefix, dockerNetwork, cli_ver.DaprGitHubRepo)
|
||||
|
||||
if slimMode {
|
||||
// Initialize placement binary only on slim install
|
||||
go installBinary(&wg, errorChan, daprBinDir, runtimeVersion, placementServiceFilePrefix, dockerNetwork)
|
||||
go installBinary(&wg, errorChan, daprBinDir, runtimeVersion, placementServiceFilePrefix, dockerNetwork, cli_ver.DaprGitHubRepo)
|
||||
} else {
|
||||
for _, step := range initSteps {
|
||||
// Run init on the configurations and containers
|
||||
|
@ -429,29 +429,29 @@ func runPlacementService(wg *sync.WaitGroup, errorChan chan<- error, dir, versio
|
|||
errorChan <- nil
|
||||
}
|
||||
|
||||
func installBinary(wg *sync.WaitGroup, errorChan chan<- error, dir, version, binaryFilePrefix string, dockerNetwork string) {
|
||||
func installBinary(wg *sync.WaitGroup, errorChan chan<- error, dir, version, binaryFilePrefix string, dockerNetwork string, githubRepo string) {
|
||||
defer wg.Done()
|
||||
|
||||
archiveExt := "tar.gz"
|
||||
|
||||
if runtime.GOOS == daprWindowsOS {
|
||||
archiveExt = "zip"
|
||||
}
|
||||
|
||||
if version == daprLatestVersion {
|
||||
var err error
|
||||
version, err = cli_ver.GetLatestRelease(cli_ver.DaprGitHubOrg, cli_ver.DaprGitHubRepo)
|
||||
version, err = cli_ver.GetLatestRelease(cli_ver.DaprGitHubOrg, githubRepo)
|
||||
if err != nil {
|
||||
errorChan <- fmt.Errorf("cannot get the latest release version: %s", err)
|
||||
return
|
||||
}
|
||||
version = version[1:]
|
||||
}
|
||||
// https://github.com/dapr/dapr/releases/download/v0.8.0/daprd_darwin_amd64.tar.gz
|
||||
// https://github.com/dapr/dapr/releases/download/v0.8.0/placement_darwin_amd64.tar.gz
|
||||
|
||||
fileURL := fmt.Sprintf(
|
||||
"https://github.com/%s/%s/releases/download/v%s/%s_%s_%s.%s",
|
||||
cli_ver.DaprGitHubOrg,
|
||||
cli_ver.DaprGitHubRepo,
|
||||
githubRepo,
|
||||
version,
|
||||
binaryFilePrefix,
|
||||
runtime.GOOS,
|
||||
|
@ -467,7 +467,7 @@ func installBinary(wg *sync.WaitGroup, errorChan chan<- error, dir, version, bin
|
|||
extractedFilePath := ""
|
||||
|
||||
if archiveExt == "zip" {
|
||||
extractedFilePath, err = unzip(filepath, dir)
|
||||
extractedFilePath, err = unzip(filepath, dir, binaryFilePrefix)
|
||||
} else {
|
||||
extractedFilePath, err = untar(filepath, dir, binaryFilePrefix)
|
||||
}
|
||||
|
@ -554,47 +554,63 @@ func makeExecutable(filepath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func unzip(filepath, targetDir string) (string, error) {
|
||||
zipReader, err := zip.OpenReader(filepath)
|
||||
// https://github.com/snyk/zip-slip-vulnerability, fixes gosec G305
|
||||
func sanitizeExtractPath(destination string, filePath string) (string, error) {
|
||||
destpath := path_filepath.Join(destination, filePath)
|
||||
if !strings.HasPrefix(destpath, path_filepath.Clean(destination)+string(os.PathSeparator)) {
|
||||
return "", fmt.Errorf("%s: illegal file path", filePath)
|
||||
}
|
||||
return destpath, nil
|
||||
}
|
||||
|
||||
func unzip(filepath, targetDir, binaryFilePrefix string) (string, error) {
|
||||
r, err := zip.OpenReader(filepath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer zipReader.Close()
|
||||
defer r.Close()
|
||||
|
||||
if len(zipReader.Reader.File) > 0 {
|
||||
file := zipReader.Reader.File[0]
|
||||
|
||||
zippedFile, err := file.Open()
|
||||
foundBinary := ""
|
||||
for _, f := range r.File {
|
||||
fpath, err := sanitizeExtractPath(targetDir, f.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer zippedFile.Close()
|
||||
|
||||
extractedFilePath := path.Join(
|
||||
targetDir,
|
||||
file.Name,
|
||||
)
|
||||
if strings.HasSuffix(fpath, fmt.Sprintf("%s.exe", binaryFilePrefix)) {
|
||||
foundBinary = fpath
|
||||
}
|
||||
|
||||
outputFile, err := os.OpenFile(
|
||||
extractedFilePath,
|
||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
|
||||
file.Mode(),
|
||||
)
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(fpath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(path_filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer outputFile.Close()
|
||||
|
||||
// #nosec G110
|
||||
_, err = io.Copy(outputFile, zippedFile)
|
||||
_, err = io.Copy(outFile, rc)
|
||||
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return extractedFilePath, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
return foundBinary, nil
|
||||
}
|
||||
|
||||
func untar(filepath, targetDir, binaryFilePrefix string) (string, error) {
|
||||
|
@ -612,41 +628,49 @@ func untar(filepath, targetDir, binaryFilePrefix string) (string, error) {
|
|||
|
||||
tr := tar.NewReader(gzr)
|
||||
|
||||
foundBinary := ""
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
return "", fmt.Errorf("file is empty")
|
||||
case err != nil:
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
case header == nil:
|
||||
} else if header == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
extractedFilePath := path.Join(targetDir, header.Name)
|
||||
// untar all files in archive
|
||||
path, err := sanitizeExtractPath(targetDir, header.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeReg:
|
||||
// Extract only the binaryFile
|
||||
if header.Name != binaryFilePrefix {
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(extractedFilePath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
info := header.FileInfo()
|
||||
if info.IsDir() {
|
||||
if err = os.MkdirAll(path, info.Mode()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// #nosec G110
|
||||
if _, err := io.Copy(f, tr); err != nil {
|
||||
return "", err
|
||||
}
|
||||
f.Close()
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return extractedFilePath, nil
|
||||
// #nosec G110
|
||||
if _, err = io.Copy(f, tr); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If the found file is the binary that we want to find, save it and return later
|
||||
if strings.HasSuffix(header.Name, binaryFilePrefix) {
|
||||
foundBinary = path
|
||||
}
|
||||
}
|
||||
return foundBinary, nil
|
||||
}
|
||||
|
||||
func moveFileToPath(filepath string, installLocation string) (string, error) {
|
||||
|
@ -716,7 +740,7 @@ func downloadFile(dir string, url string) (string, error) {
|
|||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 404 {
|
||||
return "", errors.New("runtime version not found")
|
||||
return "", fmt.Errorf("version not found from url: %s", url)
|
||||
} else if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("download failed with %d", resp.StatusCode)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue