Пример #1
0
        public async Task DeployGamePopulatesDeploymentModeAsync(
            [Values(DeployOnLaunchSetting.DELTA, DeployOnLaunchSetting.ALWAYS)]
            DeployOnLaunchSetting value, [Values] bool commandSucceeds)
        {
            string        localPath = GetLocalPath();
            IAsyncProject project   = GetProjectWithLocalPathAndDeployMode(localPath, value);

            (SshTarget target, IAction action, ICancelable cancelable) = GetDeploymentArguments();
            (_, IRemoteCommand command, IRemoteDeploy deploy, _)       = GetTestObjects();

            if (!commandSucceeds)
            {
                command.RunWithSuccessAsync(Arg.Any <SshTarget>(), Arg.Any <string>())
                .Returns(_ => throw new ProcessException(_chmodFailed));
            }

            try
            {
                await deploy.DeployGameExecutableAsync(project, target, cancelable, action);
            }
            catch (DeployException)
            {
                Assert.IsFalse(commandSucceeds);
            }

            CopyBinaryData actionEvent = action.GetEvent().CopyExecutable;

            Assert.That(actionEvent.DeploymentMode,
                        Is.EqualTo(CopyBinaryType.Types.DeploymentMode.GgpRsync));
        }
Пример #2
0
        public void DeployGameExecutablePopulatesActionEventOnFailureInDeployment(
            [Values(DeployOnLaunchSetting.DELTA, DeployOnLaunchSetting.ALWAYS)]
            DeployOnLaunchSetting value)
        {
            string        localPath = GetLocalPath();
            IAsyncProject project   = GetProjectWithLocalPathAndDeployMode(localPath, value);

            (SshTarget target, IAction action, ICancelable cancelable) = GetDeploymentArguments();
            (IRemoteFile file, _, IRemoteDeploy deploy, _)             = GetTestObjects();
            file
            .SyncAsync(Arg.Any <SshTarget>(), Arg.Any <string>(), Arg.Any <string>(),
                       Arg.Any <ICancelable>(), Arg.Any <bool>())
            .Returns <Task>(_ => throw new ProcessException(_rsyncFailed));

            var error = Assert.ThrowsAsync <DeployException>(
                async() =>
                await deploy.DeployGameExecutableAsync(
                    project, target, cancelable, action));

            Assert.Multiple(() =>
            {
                string expectedError = ErrorStrings.FailedToDeployExecutable(_rsyncFailed);
                Assert.That(error.Message, Is.EqualTo(expectedError));
                CopyBinaryData actionEvent = action.GetEvent().CopyExecutable;
                Assert.NotNull(actionEvent);
                Assert.NotNull(actionEvent.CopyBinaryBytes);
                Assert.IsTrue(actionEvent.CopyAttempted);
                Assert.That(actionEvent.CopyExitCode, Is.EqualTo(-1));
                Assert.IsNull(actionEvent.SshChmodExitCode);
            });
        }
Пример #3
0
        IAsyncProject GetProjectWithLocalPathAndDeployMode(string localPath,
                                                           DeployOnLaunchSetting
                                                           deployOnLaunchSetting)
        {
            var project = Substitute.For <IAsyncProject>();

            project.GetDeployOnLaunchAsync().Returns(deployOnLaunchSetting);
            project.GetTargetPathAsync().Returns(localPath);
            return(project);
        }
        public async Task GetGgpDeployOnLaunchAsync(string stringValue,
                                                    DeployOnLaunchSetting enumValue)
        {
            var projectPath = @"C:\GGP_project_path\";
            var projectValues = new ProjectValues
            {
                GgpDeployOnLaunch = stringValue,
            };

            var configuredProject = CreateConfiguredProject(projectValues, projectPath);
            IAsyncProject project = new ConfiguredProjectAdapter(configuredProject);
            Assert.AreEqual(enumValue, await project.GetDeployOnLaunchAsync());
        }
Пример #5
0
        public async Task DeployGamePopulatesSignatureCheckModeAsync(
            DeployOnLaunchSetting deploySetting,
            BinarySignatureCheck.Types.Result signatureCheck)
        {
            string        localPath = GetLocalPath();
            IAsyncProject project   = GetProjectWithLocalPathAndDeployMode(localPath, deploySetting);

            (SshTarget target, IAction action, ICancelable cancelable) = GetDeploymentArguments();
            (_, _, IRemoteDeploy deploy, _) = GetTestObjects();

            await deploy.DeployGameExecutableAsync(project, target, cancelable, action);

            CopyBinaryData actionEvent = action.GetEvent().CopyExecutable;

            Assert.That(actionEvent.SignatureCheckResult, Is.EqualTo(signatureCheck));
        }
Пример #6
0
        string GgpDeployOnLaunchToDisplayName(DeployOnLaunchSetting enumValue)
        {
            // These values are copied from DisplayNames in debugger_ggp.xml.
            switch (enumValue)
            {
            case DeployOnLaunchSetting.FALSE:
                return("No");

            case DeployOnLaunchSetting.DELTA:
                return("Yes - incremental");

            case DeployOnLaunchSetting.ALWAYS:
                return("Yes - always");
            }

            return("");
        }
Пример #7
0
        public async Task DeployGameExecutablePopulatesActionEventOnSuccessAsync(
            [Values(DeployOnLaunchSetting.DELTA, DeployOnLaunchSetting.ALWAYS)]
            DeployOnLaunchSetting value)
        {
            string        localPath = GetLocalPath();
            IAsyncProject project   = GetProjectWithLocalPathAndDeployMode(localPath, value);

            (SshTarget target, IAction action, ICancelable cancelable) = GetDeploymentArguments();
            (_, _, IRemoteDeploy deploy, _) = GetTestObjects();
            await deploy.DeployGameExecutableAsync(project, target, cancelable, action);

            Assert.Multiple(() =>
            {
                CopyBinaryData actionEvent = action.GetEvent().CopyExecutable;
                Assert.NotNull(actionEvent);
                Assert.NotNull(actionEvent.CopyBinaryBytes);
                Assert.IsTrue(actionEvent.CopyAttempted);
                Assert.That(actionEvent.CopyExitCode, Is.EqualTo(0));
                Assert.That(actionEvent.SshChmodExitCode, Is.EqualTo(0));
            });
        }
Пример #8
0
        /// <summary>
        /// Selects a gamelet from the given list and prepares it for running a game.
        /// </summary>
        /// <exception cref="InvalidStateException">
        /// Thrown when the selected gamelet is in an unexpected state.</exception>
        /// <exception cref="ConfigurationException">
        /// Thrown if there is no gamelet reserved</exception>
        /// <exception cref="CloudException">Thrown if there are any RPC errors.</exception>
        /// <returns>True if the gamelet was prepared successfully, false otherwise.</returns>
        public bool TrySelectAndPrepareGamelet(string targetPath,
                                               DeployOnLaunchSetting deployOnLaunchSetting,
                                               List <Gamelet> gamelets, TestAccount testAccount,
                                               string devAccount, out Gamelet gamelet)
        {
            if (!TrySelectGamelet(gamelets, out gamelet))
            {
                return(false);
            }

            if (!StopGameLaunchIfPresent(testAccount, devAccount, gamelets, gamelet))
            {
                return(false);
            }

            /// If developer runs a game with a test account first, then switches the account and
            /// tries to run a game on the same gamelet, the <see cref="StopGameLaunchIfPresent"/>
            /// won't stop the running game on the gamelet.
            if (!StopGameIfNeeded(ref gamelet))
            {
                return(false);
            }

            if (!EnableSsh(gamelet))
            {
                return(false);
            }

            if (!ValidateMountConfiguration(targetPath, deployOnLaunchSetting, gamelet))
            {
                return(false);
            }

            if (!ClearLogs(gamelet))
            {
                return(false);
            }

            return(true);
        }
Пример #9
0
        /// <summary>
        /// Selects a gamelet from the given list and prepares it for running a game.
        /// </summary>
        /// <exception cref="InvalidStateException">
        /// Thrown when the selected gamelet is in an unexpected state.</exception>
        /// <exception cref="ConfigurationException">
        /// Thrown if there is no gamelet reserved</exception>
        /// <exception cref="CloudException">Thrown if there are any RPC errors.</exception>
        /// <returns>True if the gamelet was prepared successfully, false otherwise.</returns>
        public bool TrySelectAndPrepareGamelet(string targetPath,
                                               DeployOnLaunchSetting deployOnLaunchSetting,
                                               List <Gamelet> gamelets, TestAccount testAccount,
                                               string devAccount, out Gamelet result)
        {
            Gamelet gamelet = result = null;

            if (!TrySelectGamelet(gamelets, out gamelet))
            {
                return(false);
            }

            if (gamelet.State == GameletState.InUse)
            {
                if (!PromptStopGamelet(ref gamelet))
                {
                    return(false);
                }
            }

            if (!EnableSsh(gamelet))
            {
                return(false);
            }

            if (!ValidateMountConfiguration(targetPath, deployOnLaunchSetting, gamelet))
            {
                return(false);
            }

            if (!ClearLogs(gamelet))
            {
                return(false);
            }

            result = gamelet;
            return(true);
        }
Пример #10
0
        public async Task DeployGameExecutableDeploysBinaryAndSetsExecutableBitAsync(
            [Values(DeployOnLaunchSetting.DELTA, DeployOnLaunchSetting.ALWAYS)]
            DeployOnLaunchSetting value)
        {
            string        localPath = GetLocalPath();
            IAsyncProject project   = GetProjectWithLocalPathAndDeployMode(localPath, value);

            (SshTarget target, IAction action, ICancelable cancelable)          = GetDeploymentArguments();
            (IRemoteFile file, IRemoteCommand command, IRemoteDeploy deploy, _) = GetTestObjects();

            await deploy.DeployGameExecutableAsync(project, target, cancelable, action);

            Assert.Multiple(async() =>
            {
                await file.Received(1).SyncAsync(target, localPath,
                                                 YetiConstants.RemoteDeployPath, cancelable,
                                                 Arg.Any <bool>());

                await command.Received(1)
                .RunWithSuccessAsync(
                    target, $"chmod a+x {YetiConstants.RemoteDeployPath}{_binaryName}");
            });
        }
Пример #11
0
        public async Task DeployGameExecutableAsync(IAsyncProject project, SshTarget target,
                                                    ICancelable task, Metrics.IAction action)
        {
            DataRecorder          record        = new DataRecorder(action, DataRecorder.File.GAME_EXECUTABLE);
            DeployOnLaunchSetting deploySetting = await GetDeployOnLaunchSettingAsync(project);

            if (deploySetting != DeployOnLaunchSetting.FALSE)
            {
                string localPath = await project.GetTargetPathAsync();

                bool force = (deploySetting == DeployOnLaunchSetting.ALWAYS);
                await DeployToTargetAsync(record, task, target, localPath,
                                          YetiConstants.RemoteDeployPath, force);

                string targetName = Path.GetFileName(localPath);
                string remotePath = Path.Combine(YetiConstants.RemoteDeployPath, targetName);
                await SetRemoteExecutableBitAsync(target, remotePath, record);
            }
            else
            {
                record.SetCopyAttempted(false);
                record.SignatureCheckResult(BinarySignatureCheck.Types.Result.NoCopy);
            }
        }
Пример #12
0
 public void SetDeployOnLaunch(DeployOnLaunchSetting deployOnLaunch) =>
 SetStringProperty(ProjectPropertyName.GgpDeployOnLaunch, deployOnLaunch.ToString());
Пример #13
0
        /// <summary>
        /// Check whether the deployment configuration of the binary works correctly
        /// with the mount configuration of the gamelet.
        /// </summary>
        /// <param name="targetPath">Path to the generated binary.</param>
        /// <param name="deployOnLaunchSetting">Project's "Deploy On Launch" value.</param>
        /// <param name="gamelet">Gamelet to connect to.</param>
        /// <returns>True if no issues found or the user decided to proceed.</returns>
        bool ValidateMountConfiguration(string targetPath,
                                        DeployOnLaunchSetting deployOnLaunchSetting,
                                        Gamelet gamelet)
        {
            MountConfiguration configuration =
                _mountChecker.GetConfiguration(gamelet, _actionRecorder);

            string targetPathNormalized = GetNormalizedFullPath(targetPath);

            Trace.WriteLine($"TargetPath is set to {targetPathNormalized}");
            // If the /srv/game/assets folder is detached from /mnt/developer then
            // binaries generated by VS won't be used during the run/debug process.
            // Notify the user and let them decide whether this is expected behaviour or not.
            if (_mountChecker.IsGameAssetsDetachedFromDeveloperFolder(configuration))
            {
                // 'Yes' - continue; 'No' - interrupt (gamelet validation fails).
                return(_dialogUtil.ShowYesNo(
                           ErrorStrings.MountConfigurationWarning(YetiConstants.GameAssetsMountingPoint,
                                                                  YetiConstants.DeveloperMountingPoint),
                           _mountConfigurationDialogCaption));
            }

            if (_mountChecker.IsAssetStreamingActivated(configuration))
            {
                var sshChannels = new SshTunnels();
                IEnumerable <string> commandLines = sshChannels.GetSshCommandLines();
                string[]             mountPoints  = sshChannels.ExtractMountingPoints(commandLines).ToArray();

                if (mountPoints.Length == 0)
                {
                    // If asset streaming is set up on the gamelet but there is no ssh tunnels
                    // between the workstation and the gamelet then the connection was
                    // probably lost (or asset streaming is set to a different machine, and
                    // then it's ok).
                    // 'Yes' - continue; 'No' - interrupt (gamelet validation fails).
                    return(_dialogUtil.ShowYesNo(ErrorStrings.AssetStreamingBrokenWarning(),
                                                 _mountConfigurationDialogCaption));
                }

                if (deployOnLaunchSetting != DeployOnLaunchSetting.FALSE)
                {
                    foreach (string mountPoint in mountPoints)
                    {
                        string mountPointNormalized = GetNormalizedFullPath(mountPoint);
                        if (targetPathNormalized.StartsWith($@"{mountPointNormalized}\"))
                        {
                            // The mount point folder matches the output folder for the binaries;
                            // VS will try to upload the binaries to the gamelet and this might lead
                            // to an exception during 'scp' call. Instead, asset streaming should
                            // take care of uploading the generated data to the gamelet. 'Yes' -
                            // continue; 'No' - interrupt (gamelet validation fails).
                            string current  = GgpDeployOnLaunchToDisplayName(deployOnLaunchSetting);
                            string expected =
                                GgpDeployOnLaunchToDisplayName(DeployOnLaunchSetting.FALSE);
                            return(_dialogUtil.ShowYesNo(
                                       ErrorStrings.AssetStreamingDeployWarning(mountPointNormalized,
                                                                                current, expected), _mountConfigurationDialogCaption));
                        }
                    }
                }
            }

            return(true);

            string GetNormalizedFullPath(string path)
            {
                if (string.IsNullOrWhiteSpace(path))
                {
                    return(path);
                }

                string normalizedPath = FileUtil.GetNormalizedPath(path);

                if (File.Exists(normalizedPath) && FileUtil.IsPathSymlink(normalizedPath))
                {
                    string symlinkTarget = NativeMethods.GetTargetPathName(path);
                    return(FileUtil.GetNormalizedPath(symlinkTarget));
                }

                return(normalizedPath);
            }
        }
Пример #14
0
        public async Task <IReadOnlyList <IDebugLaunchSettings> > QueryDebugTargetsAsync(
            IAsyncProject project, DebugLaunchOptions launchOptions)
        {
            try
            {
                // Make sure we can find the target executable.
                var targetPath = await project.GetTargetPathAsync();

                if (!_fileSystem.File.Exists(targetPath))
                {
                    Trace.WriteLine($"Unable to find target executable: {targetPath}");
                    _dialogUtil.ShowError(ErrorStrings.UnableToFindTargetExecutable(targetPath));
                    return(new IDebugLaunchSettings[] { });
                }

                _metrics.UseNewDebugSessionId();
                var actionRecorder = new ActionRecorder(_metrics);

                var targetFileName = await project.GetTargetFileNameAsync();

                var gameletCommand = (targetFileName + " " +
                                      await project.GetGameletLaunchArgumentsAsync()).Trim();

                var launchParams = new LaunchParams()
                {
                    Cmd       = gameletCommand,
                    RenderDoc = await project.GetLaunchRenderDocAsync(),
                    Rgp       = await project.GetLaunchRgpAsync(),
                    SurfaceEnforcementMode = await project.GetSurfaceEnforcementAsync(),
                    VulkanDriverVariant    = await project.GetVulkanDriverVariantAsync(),
                    QueryParams            = await project.GetQueryParamsAsync(),
                    Endpoint = await project.GetEndpointAsync()
                };

                if (_sdkVersion != null && !string.IsNullOrEmpty(_sdkVersion.ToString()))
                {
                    launchParams.SdkVersion = _sdkVersion.ToString();
                }

                if (!TrySetupQueries(project, actionRecorder,
                                     out SetupQueriesResult setupQueriesResult))
                {
                    return(new IDebugLaunchSettings[] { });
                }

                launchParams.ApplicationName = setupQueriesResult.Application.Name;
                launchParams.ApplicationId   = setupQueriesResult.Application.Id;
                if (setupQueriesResult.TestAccount != null)
                {
                    launchParams.TestAccount          = setupQueriesResult.TestAccount.Name;
                    launchParams.TestAccountGamerName =
                        setupQueriesResult.TestAccount.GamerStadiaName;
                }

                DeployOnLaunchSetting deployOnLaunchAsync = await project.GetDeployOnLaunchAsync();

                launchParams.Account = _credentialManager.LoadAccount();

                // TODO: Enable PlayerEndpoint Launches for non-internal usage in VS.
                if (launchParams.Endpoint == StadiaEndpoint.PlayerEndpoint &&
                    launchParams.Account != null &&
                    !launchParams.Account.EndsWith("@sparklingsunset.com") &&
                    !launchParams.Account.EndsWith("@subtlesunset.com"))
                {
                    throw new NotImplementedException(
                              "Player Endpoints are not yet supported, please select " +
                              "Test Client in the Project Properties instead.");
                }

                // TODO: Enable launch on any endpoint for external accounts.
                if (launchParams.Endpoint == StadiaEndpoint.AnyEndpoint &&
                    launchParams.Account != null &&
                    !launchParams.Account.EndsWith("@sparklingsunset.com") &&
                    !launchParams.Account.EndsWith("@subtlesunset.com"))
                {
                    throw new NotImplementedException(
                              "Launch on any player endpoint is not supported yet, please select " +
                              "another endpoint in the Project Properties instead.");
                }

                bool launchGameApiEnabled =
                    _yetiVsiService.Options.LaunchGameApiFlow == LaunchGameApiFlow.ENABLED;
                IGameletSelector gameletSelector =
                    _gameletSelectorFactory.Create(launchGameApiEnabled, actionRecorder);
                if (!gameletSelector.TrySelectAndPrepareGamelet(
                        targetPath, deployOnLaunchAsync, setupQueriesResult.Gamelets,
                        setupQueriesResult.TestAccount, launchParams.Account, out Gamelet gamelet))
                {
                    return(new IDebugLaunchSettings[] { });
                }

                launchParams.GameletName            = gamelet.Name;
                launchParams.PoolId                 = gamelet.PoolId;
                launchParams.GameletSdkVersion      = gamelet.GameletVersions.DevToolingVersion;
                launchParams.GameletEnvironmentVars =
                    await project.GetGameletEnvironmentVariablesAsync();

                // Prepare for debug launch using these settings.
                var debugLaunchSettings = new DebugLaunchSettings(launchOptions);
                debugLaunchSettings.Environment["PATH"] = await project.GetExecutablePathAsync();

                debugLaunchSettings.LaunchOperation  = DebugLaunchOperation.CreateProcess;
                debugLaunchSettings.CurrentDirectory = await project.GetAbsoluteRootPathAsync();

                if (!launchOptions.HasFlag(DebugLaunchOptions.NoDebug))
                {
                    var parameters = _paramsFactory.Create();
                    parameters.TargetIp         = new SshTarget(gamelet).GetString();
                    parameters.DebugSessionId   = _metrics.DebugSessionId;
                    debugLaunchSettings.Options = _paramsFactory.Serialize(parameters);
                }

                IAction action     = actionRecorder.CreateToolAction(ActionType.RemoteDeploy);
                bool    isDeployed = _cancelableTaskFactory.Create(
                    TaskMessages.DeployingExecutable, async task =>
                {
                    await _remoteDeploy.DeployGameExecutableAsync(
                        project, new SshTarget(gamelet), task, action);
                    task.Progress.Report(TaskMessages.CustomDeployCommand);
                    await _remoteDeploy.ExecuteCustomCommandAsync(project, gamelet, action);
                }).RunAndRecord(action);

                if (!isDeployed)
                {
                    return(new IDebugLaunchSettings[] { });
                }

                if (launchOptions.HasFlag(DebugLaunchOptions.NoDebug))
                {
                    if (_gameLauncher.LaunchGameApiEnabled ||
                        launchParams.Endpoint == StadiaEndpoint.PlayerEndpoint ||
                        launchParams.Endpoint == StadiaEndpoint.AnyEndpoint)
                    {
                        IVsiGameLaunch launch = _gameLauncher.CreateLaunch(launchParams);
                        if (launch != null)
                        {
                            debugLaunchSettings.Arguments =
                                _launchCommandFormatter.CreateWithLaunchName(
                                    launchParams, launch.LaunchName);
                        }
                        else
                        {
                            Trace.WriteLine("Unable to retrieve launch name from the launch api.");
                            return(new IDebugLaunchSettings[] { });
                        }
                    }
                    else
                    {
                        debugLaunchSettings.Arguments =
                            _launchCommandFormatter.CreateFromParams(launchParams);
                    }

                    debugLaunchSettings.Executable =
                        Path.Combine(Environment.SystemDirectory, YetiConstants.Command);

                    debugLaunchSettings.LaunchOptions = DebugLaunchOptions.NoDebug |
                                                        DebugLaunchOptions.MergeEnvironment;
                }
                else
                {
                    if (_yetiVsiService.DebuggerOptions[DebuggerOption.SKIP_WAIT_LAUNCH] ==
                        DebuggerOptionState.DISABLED)
                    {
                        launchParams.Debug = true;
                    }

                    // TODO: This should really be the game_client executable, since
                    // the args we pass are for game_client as well.  We just need to find another
                    // way to pass the game executable.
                    debugLaunchSettings.Executable            = targetPath;
                    debugLaunchSettings.LaunchDebugEngineGuid = YetiConstants.DebugEngineGuid;
                    debugLaunchSettings.Arguments             =
                        _launchCommandFormatter.EncodeLaunchParams(launchParams);
                    debugLaunchSettings.LaunchOptions = DebugLaunchOptions.MergeEnvironment;
                }

                return(new IDebugLaunchSettings[] { debugLaunchSettings });
            }
            catch (Exception e)
            {
                Trace.WriteLine(e.ToString());
                _dialogUtil.ShowError(e.Message, e.ToString());
                return(new IDebugLaunchSettings[] { });
            }
        }