<commit_before>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package test

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/runutil"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
)

const (
	stagingBlessingsRoot    = "dev.staging.v.io" // TODO(jingjin): use a better name and update prod.go.
	localMountTable         = "/ns.dev.staging.v.io:8151"
	globalMountTable        = "/ns.dev.staging.v.io:8101"
	numRetries              = 30
	objNameForDeviceManager = "devices/vanadium-cell-master/devmgr/device"
	retryPeriod             = 10 * time.Second
)

var (
	defaultReleaseTestTimeout = time.Minute * 5

	serviceBinaries = []string{
		"binaryd",
		"applicationd",
		"proxyd",
		"identityd",
		"mounttabled",
		"roled",
		"deviced",
	}
)

// vanadiumReleaseTest updates binaries of staging cloud services and run tests for them.
func vanadiumReleaseTest(ctx *tool.Context, testName string, opts ...Opt) (_ *test.Result, e error) {
	root, err := util.V23Root()
	if err != nil {
		return nil, err
	}

	cleanup, err := initTest(ctx, testName, []string{})
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	type step struct {
		msg string
		fn  func() error
	}
	adminCredDir, publisherCredDir := getCredDirOptValues(opts)
	steps := []step{
		step{
			msg: fmt.Sprintf("Fetching credentials from %v (admin) and %v (publisher)\n", adminCredDir, publisherCredDir),
			fn: func() error {
				if _, err := os.Stat(adminCredDir); err != nil {
					return err
				}
				if _, err := os.Stat(publisherCredDir); err != nil {
					return err
				}
				return nil
			},
		},
		step{
			msg: "Build binaries\n",
			fn:  func() error { return buildBinaries(ctx, root) },
		},
		step{
			msg: "Update services\n",
			fn:  func() error { return updateServices(ctx, root, adminCredDir, publisherCredDir) },
		},
		step{
			msg: "Check services\n",
			fn:  func() error { return checkServices(ctx) },
		},
		step{
			msg: "Create snapshot\n",
			fn:  func() error { return createSnapshot(ctx) },
		},
	}
	for _, step := range steps {
		if result, err := invoker(ctx, step.msg, step.fn); result != nil || err != nil {
			return result, err
		}
	}
	return &test.Result{Status: test.Passed}, nil
}

// invoker invokes the given function and returns test.Result and/or
// errors based on function's results.
func invoker(ctx *tool.Context, msg string, fn func() error) (*test.Result, error) {
	if err := fn(); err != nil {
		test.Fail(ctx, msg)
		if err == runutil.CommandTimedOutErr {
			return &test.Result{
				Status:       test.TimedOut,
				TimeoutValue: defaultReleaseTestTimeout,
			}, nil
		}
		fmt.Fprintf(ctx.Stderr(), "%s\n", err.Error())
		return nil, internalTestError{err, msg}
	}
	test.Pass(ctx, msg)
	return nil, nil
}

// getCredDirOptValues returns the values of credentials dirs (admin, publisher)
// from the given Opt slice.
func getCredDirOptValues(opts []Opt) (string, string) {
	adminCredDir, publisherCredDir := "", ""
	for _, opt := range opts {
		switch v := opt.(type) {
		case AdminCredDirOpt:
			adminCredDir = string(v)
		case PublisherCredDirOpt:
			publisherCredDir = string(v)
		}
	}
	return adminCredDir, publisherCredDir
}

// buildBinaries builds all vanadium binaries.
func buildBinaries(ctx *tool.Context, root string) error {
	args := []string{"go", "install", "v.io/..."}
	if err := ctx.Run().Command("v23", args...); err != nil {
		return err
	}
	return nil
}

// updateServices pushes services' binaries to the applications and binaries
// services and tells the device manager to update all its app.
func updateServices(ctx *tool.Context, root, adminCredDir, publisherCredDir string) (e error) {
	deviceBin := filepath.Join(root, "release", "go", "bin", "device")
	adminCredentialsArg := fmt.Sprintf("--v23.credentials=%s", adminCredDir)
	publisherCredentialsArg := fmt.Sprintf("--v23.credentials=%s", publisherCredDir)
	nsArg := fmt.Sprintf("--v23.namespace.root=%s", globalMountTable)

	// Push all binaries.
	{
		fmt.Fprintln(ctx.Stdout(), "Pushing binaries...")
		args := []string{
			publisherCredentialsArg,
			nsArg,
			"publish",
			"--goos=linux",
			"--goarch=amd64",
		}
		args = append(args, serviceBinaries...)
		if err := ctx.Run().TimedCommand(defaultReleaseTestTimeout, deviceBin, args...); err != nil {
			return err
		}
	}

	// A helper function to update a single app.
	updateAppFn := func(appName string) error {
		args := []string{
			publisherCredentialsArg,
			fmt.Sprintf("--v23.namespace.root=%s", localMountTable),
			"updateall",
			appName,
		}
		if err := ctx.Run().TimedCommand(defaultReleaseTestTimeout, deviceBin, args...); err != nil {
			return err
		}
		return nil
	}

	// Update services except for device manager and mounttable.
	{<commit_msg>devtools: v23/internal/test: updateall is no more (v.io/c/11758)<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package test

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"v.io/x/devtools/internal/collect"
	"v.io/x/devtools/internal/runutil"
	"v.io/x/devtools/internal/test"
	"v.io/x/devtools/internal/tool"
	"v.io/x/devtools/internal/util"
)

const (
	stagingBlessingsRoot    = "dev.staging.v.io" // TODO(jingjin): use a better name and update prod.go.
	localMountTable         = "/ns.dev.staging.v.io:8151"
	globalMountTable        = "/ns.dev.staging.v.io:8101"
	numRetries              = 30
	objNameForDeviceManager = "devices/vanadium-cell-master/devmgr/device"
	retryPeriod             = 10 * time.Second
)

var (
	defaultReleaseTestTimeout = time.Minute * 5

	serviceBinaries = []string{
		"binaryd",
		"applicationd",
		"proxyd",
		"identityd",
		"mounttabled",
		"roled",
		"deviced",
	}
)

// vanadiumReleaseTest updates binaries of staging cloud services and run tests for them.
func vanadiumReleaseTest(ctx *tool.Context, testName string, opts ...Opt) (_ *test.Result, e error) {
	root, err := util.V23Root()
	if err != nil {
		return nil, err
	}

	cleanup, err := initTest(ctx, testName, []string{})
	if err != nil {
		return nil, internalTestError{err, "Init"}
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	type step struct {
		msg string
		fn  func() error
	}
	adminCredDir, publisherCredDir := getCredDirOptValues(opts)
	steps := []step{
		step{
			msg: fmt.Sprintf("Fetching credentials from %v (admin) and %v (publisher)\n", adminCredDir, publisherCredDir),
			fn: func() error {
				if _, err := os.Stat(adminCredDir); err != nil {
					return err
				}
				if _, err := os.Stat(publisherCredDir); err != nil {
					return err
				}
				return nil
			},
		},
		step{
			msg: "Build binaries\n",
			fn:  func() error { return buildBinaries(ctx, root) },
		},
		step{
			msg: "Update services\n",
			fn:  func() error { return updateServices(ctx, root, adminCredDir, publisherCredDir) },
		},
		step{
			msg: "Check services\n",
			fn:  func() error { return checkServices(ctx) },
		},
		step{
			msg: "Create snapshot\n",
			fn:  func() error { return createSnapshot(ctx) },
		},
	}
	for _, step := range steps {
		if result, err := invoker(ctx, step.msg, step.fn); result != nil || err != nil {
			return result, err
		}
	}
	return &test.Result{Status: test.Passed}, nil
}

// invoker invokes the given function and returns test.Result and/or
// errors based on function's results.
func invoker(ctx *tool.Context, msg string, fn func() error) (*test.Result, error) {
	if err := fn(); err != nil {
		test.Fail(ctx, msg)
		if err == runutil.CommandTimedOutErr {
			return &test.Result{
				Status:       test.TimedOut,
				TimeoutValue: defaultReleaseTestTimeout,
			}, nil
		}
		fmt.Fprintf(ctx.Stderr(), "%s\n", err.Error())
		return nil, internalTestError{err, msg}
	}
	test.Pass(ctx, msg)
	return nil, nil
}

// getCredDirOptValues returns the values of credentials dirs (admin, publisher)
// from the given Opt slice.
func getCredDirOptValues(opts []Opt) (string, string) {
	adminCredDir, publisherCredDir := "", ""
	for _, opt := range opts {
		switch v := opt.(type) {
		case AdminCredDirOpt:
			adminCredDir = string(v)
		case PublisherCredDirOpt:
			publisherCredDir = string(v)
		}
	}
	return adminCredDir, publisherCredDir
}

// buildBinaries builds all vanadium binaries.
func buildBinaries(ctx *tool.Context, root string) error {
	args := []string{"go", "install", "v.io/..."}
	if err := ctx.Run().Command("v23", args...); err != nil {
		return err
	}
	return nil
}

// updateServices pushes services' binaries to the applications and binaries
// services and tells the device manager to update all its app.
func updateServices(ctx *tool.Context, root, adminCredDir, publisherCredDir string) (e error) {
	deviceBin := filepath.Join(root, "release", "go", "bin", "device")
	adminCredentialsArg := fmt.Sprintf("--v23.credentials=%s", adminCredDir)
	publisherCredentialsArg := fmt.Sprintf("--v23.credentials=%s", publisherCredDir)
	nsArg := fmt.Sprintf("--v23.namespace.root=%s", globalMountTable)

	// Push all binaries.
	{
		fmt.Fprintln(ctx.Stdout(), "Pushing binaries...")
		args := []string{
			publisherCredentialsArg,
			nsArg,
			"publish",
			"--goos=linux",
			"--goarch=amd64",
		}
		args = append(args, serviceBinaries...)
		if err := ctx.Run().TimedCommand(defaultReleaseTestTimeout, deviceBin, args...); err != nil {
			return err
		}
	}

	// A helper function to update a single app.
	updateAppFn := func(appName string) error {
		args := []string{
			publisherCredentialsArg,
			fmt.Sprintf("--v23.namespace.root=%s", localMountTable),
			"update",
			appName + "/...",
		}
		if err := ctx.Run().TimedCommand(defaultReleaseTestTimeout, deviceBin, args...); err != nil {
			return err
		}
		return nil
	}

	// Update services except for device manager and mounttable.
	{
{'fraction_non_alphanumeric': 0.14495902044040684, 'fraction_numerical': 0.006813468944406043, 'mean_word_length': 3.5376344086021505, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5082253', 'n_tokens_mistral': 3739, 'n_tokens_neox': 3399, 'n_words': 918}