// Run runs the update, returns true if update has been found and installed, otherwise false
190758
func (u *Updater) Run(setters ...optionSetter) bool {
	opt := &Options{Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, Silent: false} // default options

	for _, setter := range setters {
		setter.Set(opt)
	}

	writef := func(s string, a ...interface{}) {
		if !opt.Silent {
			opt.Stdout.Write([]byte(fmt.Sprintf(s, a...)))
		}
	}

	has, v := u.HasUpdate()
	if has {

		var scanner *bufio.Scanner
		if opt.Stdin != nil {
			scanner = bufio.NewScanner(opt.Stdin)
		}

		shouldProceedUpdate := func() bool {
			return shouldProceedUpdate(scanner)
		}

		writef("\nA newer version has been found[%s > %s].\n"+
			"Release notes: %s\n"+
			"Update now?[%s]: ",
			u.latestVersion.String(), u.currentVersion.String(),
			fmt.Sprintf("https://github.com/%s/%s/releases/latest", u.owner, u.repo),
			DefaultUpdaterYesInput[0]+"/n")

		if shouldProceedUpdate() {
			if !opt.Silent {
				finish := ShowIndicator(opt.Stdout, true)

				defer func() {
					finish <- true
				}()
			}
			// go get -u github.com/:owner/:repo
			cmd := exec.Command("go", "get", "-u", fmt.Sprintf("github.com/%s/%s", u.owner, u.repo))
			cmd.Stdout = opt.Stdout
			cmd.Stderr = opt.Stderr

			if err := cmd.Run(); err != nil {
				writef("\nError while trying to get the package: %s.", err.Error())
			}

			writef("\010\010\010") // remove the loading bars
			writef("Update has been installed, current version: %s. Please re-start your App.\n", u.latestVersion.String())

			// TODO: normally, this should be in dev-mode machine, so a 'go build' and' & './$executable' on the current working path should be ok
			// for now just log a message to re-run the app manually
			//writef("\nUpdater was not able to re-build and re-run your updated App.\nPlease run your App again, by yourself.")
			return true
		}

	} else {
		writef(fmt.Sprintf(DefaultUpdaterAlreadyInstalledMessage, v))
	}

	return false
}
189763
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		name       string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool
		soft     bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&name, "name", "", "")
	flags.StringVar(&name, "n", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", defaultParallel, "")
	flags.IntVar(&parallel, "p", defaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&soft, "soft", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flags
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.\n")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner username.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		origin, err := gitconfig.OriginURL()
		if err == nil {
			owner = retrieveOwnerName(origin)
		}
		if len(owner) == 0 {
			owner, err = gitconfig.GithubUser()
			if err != nil {
				owner, err = gitconfig.Username()
			}

			if err != nil {
				PrintRedf(cli.errStream,
					"Failed to set up ghr: repository owner name not found\n")
				fmt.Fprintf(cli.errStream,
					"Please set it via `-u` option.\n\n"+
						"You can set default owner name in `github.username` or `user.name`\n"+
						"in `~/.gitconfig` file\n")
				return ExitCodeOwnerNotFound
			}
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeRepoNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub API token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API URL. Base URL can also be provided via env var for use with GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s\n", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	Debugf("Name: %s", name)

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(name),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()

	if soft {
		_, err := ghr.GitHub.GetRelease(ctx, *req.TagName)

		if err == nil {
			fmt.Fprintf(cli.outStream, "ghr aboted since tag `%s` already exists\n", *req.TagName)
			return ExitCodeOK
		}

		if err != nil && err != ErrReleaseNotFound {
			PrintRedf(cli.errStream, "Failed to get GitHub release: %s\n", err)
			return ExitCodeError
		}
	}

	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s\n", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s\n", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s\n", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s\n", err)
		return ExitCodeError
	}

	if !draft {
		_, err := ghr.GitHub.EditRelease(ctx, *release.ID, &github.RepositoryRelease{
			Draft: github.Bool(false),
		})
		if err != nil {
			PrintRedf(cli.errStream, "Failed to publish release: %s\n", err)
			return ExitCodeError
		}
	}

	return ExitCodeOK
}
695468
func (c *Cron) Run() {
	if c.running {
		return
	}
	c.running = true
	c.run()
}
580955
func (e *SaltpackSign) Run(m libkb.MetaContext) error {
	if err := e.loadKey(m); err != nil {
		return err
	}

	saltpackVersion, err := libkb.SaltpackVersionFromArg(e.arg.Opts.SaltpackVersion)
	if err != nil {
		return err
	}

	if e.arg.Opts.Detached {
		return libkb.SaltpackSignDetached(e.G(), e.arg.Source, e.arg.Sink, e.key, e.arg.Opts.Binary, saltpackVersion)
	}

	return libkb.SaltpackSign(e.G(), e.arg.Source, e.arg.Sink, e.key, e.arg.Opts.Binary, saltpackVersion)
}
1320246
func (c *runner) Run(name string, arg ...string) error {
	return c.RunWithOptions(CommandOpts{}, name, arg...)
}
1284696
func (c *CmdSimpleFSSymlink) Run() error {
	cli, err := GetSimpleFSClient(c.G())
	if err != nil {
		return err
	}

	ctx := context.TODO()
	arg := keybase1.SimpleFSSymlinkArg{
		Target: c.target,
		Link:   c.link,
	}

	return cli.SimpleFSSymlink(ctx, arg)
}
311388
func (r *runner) Run(ctx context.Context, args *pb.RunnerArgs) (*pb.Build, error) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	// Validate and normalize parameters.
	args = proto.Clone(args).(*pb.RunnerArgs)
	if err := normalizeArgs(args); err != nil {
		return nil, errors.Annotate(err, "invalid args").Err()
	}

	// Print our input.
	argsJSON, err := indentedJSONPB(args)
	if err != nil {
		return nil, err
	}
	logging.Infof(ctx, "RunnerArgs: %s", argsJSON)

	// Prepare workdir.
	if err := r.setupWorkDir(args.WorkDir); err != nil {
		return nil, err
	}

	// Prepare auth contexts.
	systemAuth, userAuth, err := setupAuth(ctx, args)
	if err != nil {
		return nil, err
	}
	defer systemAuth.Close()
	defer userAuth.Close()

	// Prepare a build listener.
	var listenerErr error
	var listenerErrMU sync.Mutex
	listener := newBuildListener(streamNamePrefix, func(err error) {
		logging.Errorf(ctx, "%s", err)

		listenerErrMU.Lock()
		defer listenerErrMU.Unlock()
		if listenerErr == nil {
			listenerErr = err
			logging.Warningf(ctx, "canceling the user subprocess")
			cancel()
		}
	})

	// Start a local LogDog server.
	logdogServ, err := r.startLogDog(ctx, args, systemAuth, listener)
	if err != nil {
		return nil, errors.Annotate(err, "failed to start local logdog server").Err()
	}
	defer func() {
		if logdogServ != nil {
			_ = logdogServ.Stop()
		}
	}()

	// Run the user executable.
	err = r.runUserExecutable(ctx, args, userAuth, logdogServ, streamNamePrefix)
	if err != nil {
		return nil, err
	}

	// Wait for logdog server to stop before returning the build.
	if err := logdogServ.Stop(); err != nil {
		return nil, errors.Annotate(err, "failed to stop logdog server").Err()
	}
	logdogServ = nil // do not stop for the second time.

	// Check listener error.
	listenerErrMU.Lock()
	err = listenerErr
	listenerErrMU.Unlock()
	if err != nil {
		return nil, err
	}

	// Read the final build state.
	build := listener.Build()
	if build == nil {
		return nil, errors.Reason("user executable did not send a build").Err()
	}

	// Print the final build state.
	buildJSON, err := indentedJSONPB(build)
	if err != nil {
		return build, err
	}
	logging.Infof(ctx, "final build state: %s", buildJSON)

	// TODO(nodir): make a final UpdateBuild call.

	return build, nil
}
757721
func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) {
	args := []string{"run", "--bundle", bundle}
	if opts != nil {
		oargs, err := opts.args()
		if err != nil {
			return -1, err
		}
		args = append(args, oargs...)
	}
	cmd := r.command(context, append(args, id)...)
	if opts != nil && opts.IO != nil {
		opts.Set(cmd)
	}
	ec, err := Monitor.Start(cmd)
	if err != nil {
		return -1, err
	}
	return Monitor.Wait(cmd, ec)
}
816999
func (h *healthcheckManager) run() {
	for {
		select {
		case <-h.quit:
			h.unmarkAllBackends()
			h.stopped <- true
		case vc := <-h.vcc:
			h.update(vc.vserverName, vc.checks)
		}
	}
}
975209
func (cmd AppCmd) Run() {
	utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args)
	if err := cmd.Cmd.Run(); err != nil {
		utils.Logger.Fatal("Error running:", "error", err)
	}
}
793110
func (s *UserMatch) Run() (err error) {
	var hasValidEmail = false

	for _, email := range s.userInfo["emails"].([]interface{}) {

		if email.(map[string]interface{})["value"].(string) == s.username {
			hasValidEmail = true
			s.successFunc()
		}
	}

	if !hasValidEmail {
		s.failFunc()
		err = ErrNotValidActionForUser
	}
	return
}
1266554
func run(dir, command string, args ...string) (string, error) {
	logger.Debugf("(%s) %s %v", dir, command, args)

	cmd := exec.Command(command, args...)
	if dir != "" {
		cmd.Dir = dir
	}

	out, err := cmd.CombinedOutput()
	output := string(out)

	logger.Debugf("output: %v", output)
	return output, err
}
1079728
func (c *CmdGitMdget) Run() error {
	cli, err := GetGitClient(c.G())
	ctx := context.Background()
	if err != nil {
		return err
	}

	var res []keybase1.GitRepoResult
	if len(c.folder) > 0 {
		folder, err := ParseTLF(c.folder)
		if err != nil {
			return err
		}
		res, err = cli.GetGitMetadata(ctx, folder)
		if err != nil {
			return err
		}
	} else {
		res, err = cli.GetAllGitMetadata(ctx)
		if err != nil {
			return err
		}
	}

	jsonStr, err := json.MarshalIndent(res, "", "    ")
	if err != nil {
		return err
	}

	fmt.Println(string(jsonStr))
	return nil
}
642054
func (c *AppConfig) Run() (*AppResult, error) {
	env, buildenv, parameters, err := c.validate()

	if err != nil {
		return nil, err
	}
	// TODO: I don't belong here
	c.ensureDockerSearch()

	resolved, err := Resolve(c)
	if err != nil {
		return nil, err
	}

	repositories := resolved.Repositories
	components := resolved.Components

	if len(repositories) == 0 && len(components) == 0 {
		return nil, ErrNoInputs
	}

	if err := c.validateBuilders(components); err != nil {
		return nil, err
	}

	if len(c.Name) > 0 {
		if err := validateEnforcedName(c.Name); err != nil {
			return nil, err
		}
	}

	if len(c.To) > 0 {
		if err := validateOutputImageReference(c.To); err != nil {
			return nil, err
		}
	}

	if len(components.ImageComponentRefs().Group()) > 1 && len(c.Name) > 0 {
		return nil, errors.New("only one component or source repository can be used when specifying a name")
	}
	if len(components.UseSource()) > 1 && len(c.To) > 0 {
		return nil, errors.New("only one component with source can be used when specifying an output image reference")
	}

	// identify if there are installable components in the input provided by the user
	installables, name, err := c.installComponents(components, env)
	if err != nil {
		return nil, err
	}
	if len(installables) > 0 {
		return &AppResult{
			List:      &kapi.List{Items: installables},
			Name:      name,
			Namespace: c.OriginNamespace,

			GeneratedJobs: true,
		}, nil
	}

	pipelines, err := c.buildPipelines(components.ImageComponentRefs(), env, buildenv)
	if err != nil {
		return nil, err
	}

	acceptors := app.Acceptors{app.NewAcceptUnique(c.Typer), app.AcceptNew,
		app.NewAcceptNonExistentImageStream(c.Typer, c.ImageClient, c.OriginNamespace), app.NewAcceptNonExistentImageStreamTag(c.Typer, c.ImageClient, c.OriginNamespace)}
	objects := app.Objects{}
	accept := app.NewAcceptFirst()
	for _, p := range pipelines {
		accepted, err := p.Objects(accept, acceptors)
		if err != nil {
			return nil, fmt.Errorf("can't setup %q: %v", p.From, err)
		}
		objects = append(objects, accepted...)
	}

	objects = app.AddServices(objects, false)

	templateProcessor := templateclientv1.NewTemplateProcessorClient(c.TemplateClient.RESTClient(), c.OriginNamespace)
	templateName, templateObjects, err := c.buildTemplates(components.TemplateComponentRefs(), parameters, env, buildenv, templateProcessor)
	if err != nil {
		return nil, err
	}

	// check for circular reference specifically from the template objects and print warnings if they exist
	err = c.checkCircularReferences(templateObjects)
	if err != nil {
		if err, ok := err.(app.CircularOutputReferenceError); ok {
			// templates only apply to `oc new-app`
			addOn := ""
			if len(c.Name) == 0 {
				addOn = ", override artifact names with --name"
			}
			fmt.Fprintf(c.ErrOut, "--> WARNING: %v\n%s", err, addOn)
		} else {
			return nil, err
		}
	}

	// Ensure that extra tags are not being created
	objects, err = c.removeRedundantTags(objects)
	if err != nil {
		return nil, err
	}

	// check for circular reference specifically from the newly generated objects, handling new-app vs. new-build nuances as needed
	err = c.checkCircularReferences(objects)
	if err != nil {
		if err, ok := err.(app.CircularOutputReferenceError); ok {
			if c.ExpectToBuild {
				// circular reference handling for `oc new-build`.
				if len(c.To) == 0 {
					// Output reference was generated, return error.
					return nil, fmt.Errorf("%v, set a different tag with --to", err)
				}
				// Output reference was explicitly provided, print warning.
				fmt.Fprintf(c.ErrOut, "--> WARNING: %v\n", err)
			} else {
				// circular reference handling for `oc new-app`
				if len(c.Name) == 0 {
					return nil, fmt.Errorf("%v, override artifact names with --name", err)
				}
				// Output reference was explicitly provided, print warning.
				fmt.Fprintf(c.ErrOut, "--> WARNING: %v\n", err)
			}
		} else {
			return nil, err
		}
	}

	objects = append(objects, templateObjects...)

	name = c.Name
	if len(name) == 0 {
		name = templateName
	}
	if len(name) == 0 {
		for _, pipeline := range pipelines {
			if pipeline.Deployment != nil {
				name = pipeline.Deployment.Name
				break
			}
		}
	}
	if len(name) == 0 {
		for _, obj := range objects {
			if bc, ok := obj.(*buildv1.BuildConfig); ok {
				name = bc.Name
				break
			}
		}
	}
	if len(c.SourceSecret) > 0 {
		if len(validation.ValidateSecretName(c.SourceSecret, false)) != 0 {
			return nil, fmt.Errorf("source secret name %q is invalid", c.SourceSecret)
		}
		for _, obj := range objects {
			if bc, ok := obj.(*buildv1.BuildConfig); ok {
				klog.V(4).Infof("Setting source secret for build config to: %v", c.SourceSecret)
				bc.Spec.Source.SourceSecret = &corev1.LocalObjectReference{Name: c.SourceSecret}
				break
			}
		}
	}
	if len(c.PushSecret) > 0 {
		if len(validation.ValidateSecretName(c.PushSecret, false)) != 0 {
			return nil, fmt.Errorf("push secret name %q is invalid", c.PushSecret)
		}
		for _, obj := range objects {
			if bc, ok := obj.(*buildv1.BuildConfig); ok {
				klog.V(4).Infof("Setting push secret for build config to: %v", c.SourceSecret)
				bc.Spec.Output.PushSecret = &corev1.LocalObjectReference{Name: c.PushSecret}
				break
			}
		}
	}

	return &AppResult{
		List:      &kapi.List{Items: objects},
		Name:      name,
		HasSource: len(repositories) != 0,
		Namespace: c.OriginNamespace,
	}, nil
}
1050752
func (step commonDeploymentUpgradeStep) Run(ctx context.ProviderCallContext) error {
	env := step.env
	isControllerEnviron, err := isControllerEnviron(env, ctx)
	if err != nil {
		return errors.Trace(err)
	}
	if isControllerEnviron {
		// We only need to create the deployment for
		// non-controller Environs.
		return nil
	}

	// Identify the network security rules that exist already.
	// We will add these, excluding the SSH/API rules, to the
	// network security group template created in the deployment
	// below.
	nsgClient := network.SecurityGroupsClient{
		BaseClient: env.network,
	}
	allRules, err := networkSecurityRules(nsgClient, env.resourceGroup)
	if errors.IsNotFound(err) {
		allRules = nil
	} else if err != nil {
		return errors.Trace(err)
	}
	rules := make([]network.SecurityRule, 0, len(allRules))
	for _, rule := range allRules {
		name := to.String(rule.Name)
		if name == sshSecurityRuleName || strings.HasPrefix(name, apiSecurityRulePrefix) {
			continue
		}
		rules = append(rules, rule)
	}

	env.mu.Lock()
	storageAccountType := env.config.storageAccountType
	env.mu.Unlock()
	return env.createCommonResourceDeployment(ctx, nil, rules, storageAccountTemplateResource(
		env.location, nil,
		env.storageAccountName,
		storageAccountType,
	))
}
791881
func (u *Updater) Run(ctx context.Context, slots []orchestrator.Slot) {
	defer close(u.doneChan)

	service := u.newService

	// If the update is in a PAUSED state, we should not do anything.
	if service.UpdateStatus != nil &&
		(service.UpdateStatus.State == api.UpdateStatus_PAUSED ||
			service.UpdateStatus.State == api.UpdateStatus_ROLLBACK_PAUSED) {
		return
	}

	var dirtySlots []orchestrator.Slot
	for _, slot := range slots {
		if u.isSlotDirty(slot) {
			dirtySlots = append(dirtySlots, slot)
		}
	}
	// Abort immediately if all tasks are clean.
	if len(dirtySlots) == 0 {
		if service.UpdateStatus == nil {
			if u.annotationsUpdated(service) {
				// Annotation-only update; mark the update as completed
				u.completeUpdate(ctx, service.ID, true)
			}
			return
		}
		if service.UpdateStatus.State == api.UpdateStatus_UPDATING || service.UpdateStatus.State == api.UpdateStatus_ROLLBACK_STARTED {
			// Update or rollback was started, and is now complete
			u.completeUpdate(ctx, service.ID, true)
			return
		}
		return
	}

	// If there's no update in progress, we are starting one.
	if service.UpdateStatus == nil {
		u.startUpdate(ctx, service.ID)
	}

	var (
		monitoringPeriod time.Duration
		updateConfig     *api.UpdateConfig
	)

	if service.UpdateStatus != nil && service.UpdateStatus.State == api.UpdateStatus_ROLLBACK_STARTED {
		monitoringPeriod, _ = gogotypes.DurationFromProto(defaults.Service.Rollback.Monitor)
		updateConfig = service.Spec.Rollback
		if updateConfig == nil {
			updateConfig = defaults.Service.Rollback
		}
	} else {
		monitoringPeriod, _ = gogotypes.DurationFromProto(defaults.Service.Update.Monitor)
		updateConfig = service.Spec.Update
		if updateConfig == nil {
			updateConfig = defaults.Service.Update
		}
	}

	parallelism := int(updateConfig.Parallelism)
	if updateConfig.Monitor != nil {
		newMonitoringPeriod, err := gogotypes.DurationFromProto(updateConfig.Monitor)
		if err == nil {
			monitoringPeriod = newMonitoringPeriod
		}
	}

	if parallelism == 0 {
		// TODO(aluzzardi): We could try to optimize unlimited parallelism by performing updates in a single
		// goroutine using a batch transaction.
		parallelism = len(dirtySlots)
	}

	// Start the workers.
	slotQueue := make(chan orchestrator.Slot)
	wg := sync.WaitGroup{}
	wg.Add(parallelism)
	for i := 0; i < parallelism; i++ {
		go func() {
			u.worker(ctx, slotQueue, updateConfig)
			wg.Done()
		}()
	}

	var failedTaskWatch chan events.Event

	if updateConfig.FailureAction != api.UpdateConfig_CONTINUE {
		var cancelWatch func()
		failedTaskWatch, cancelWatch = state.Watch(
			u.store.WatchQueue(),
			api.EventUpdateTask{
				Task:   &api.Task{ServiceID: service.ID, Status: api.TaskStatus{State: api.TaskStateRunning}},
				Checks: []api.TaskCheckFunc{api.TaskCheckServiceID, state.TaskCheckStateGreaterThan},
			},
		)
		defer cancelWatch()
	}

	stopped := false
	failedTasks := make(map[string]struct{})
	totalFailures := 0

	failureTriggersAction := func(failedTask *api.Task) bool {
		// Ignore tasks we have already seen as failures.
		if _, found := failedTasks[failedTask.ID]; found {
			return false
		}

		// If this failed/completed task is one that we
		// created as part of this update, we should
		// follow the failure action.
		u.updatedTasksMu.Lock()
		startedAt, found := u.updatedTasks[failedTask.ID]
		u.updatedTasksMu.Unlock()

		if found && (startedAt.IsZero() || time.Since(startedAt) <= monitoringPeriod) {
			failedTasks[failedTask.ID] = struct{}{}
			totalFailures++
			if float32(totalFailures)/float32(len(dirtySlots)) > updateConfig.MaxFailureRatio {
				switch updateConfig.FailureAction {
				case api.UpdateConfig_PAUSE:
					stopped = true
					message := fmt.Sprintf("update paused due to failure or early termination of task %s", failedTask.ID)
					u.pauseUpdate(ctx, service.ID, message)
					return true
				case api.UpdateConfig_ROLLBACK:
					// Never roll back a rollback
					if service.UpdateStatus != nil && service.UpdateStatus.State == api.UpdateStatus_ROLLBACK_STARTED {
						message := fmt.Sprintf("rollback paused due to failure or early termination of task %s", failedTask.ID)
						u.pauseUpdate(ctx, service.ID, message)
						return true
					}
					stopped = true
					message := fmt.Sprintf("update rolled back due to failure or early termination of task %s", failedTask.ID)
					u.rollbackUpdate(ctx, service.ID, message)
					return true
				}
			}
		}

		return false
	}

slotsLoop:
	for _, slot := range dirtySlots {
	retryLoop:
		for {
			// Wait for a worker to pick up the task or abort the update, whichever comes first.
			select {
			case <-u.stopChan:
				stopped = true
				break slotsLoop
			case ev := <-failedTaskWatch:
				if failureTriggersAction(ev.(api.EventUpdateTask).Task) {
					break slotsLoop
				}
			case slotQueue <- slot:
				break retryLoop
			}
		}
	}

	close(slotQueue)
	wg.Wait()

	if !stopped {
		// Keep watching for task failures for one more monitoringPeriod,
		// before declaring the update complete.
		doneMonitoring := time.After(monitoringPeriod)
	monitorLoop:
		for {
			select {
			case <-u.stopChan:
				stopped = true
				break monitorLoop
			case <-doneMonitoring:
				break monitorLoop
			case ev := <-failedTaskWatch:
				if failureTriggersAction(ev.(api.EventUpdateTask).Task) {
					break monitorLoop
				}
			}
		}
	}

	// TODO(aaronl): Potentially roll back the service if not enough tasks
	// have reached RUNNING by this point.

	if !stopped {
		u.completeUpdate(ctx, service.ID, false)
	}
}
663118
func (c *CharmResourcesCommand) Run(ctx *cmd.Context) error {
	return c.baseRun(ctx)
}