private async Task <GameState> LoadState(TopoMojo.Api.Data.Gamespace gamespace, bool preview = false) { var state = Mapper.Map <GameState>(gamespace); state.Markdown = await LoadMarkdown(gamespace.Workspace?.Id) ?? $"# {gamespace.Name}"; if (!preview && !gamespace.HasStarted) { state.Markdown = state.Markdown.Split("<!-- cut -->").FirstOrDefault(); } if (gamespace.IsActive) { state.Vms = (await _pod.Find(gamespace.Id)) .Select(vm => new VmState { Id = vm.Id, Name = vm.Name.Untagged(), IsolationId = vm.Name.Tag(), IsRunning = (vm.State == VmPowerState.Running), IsVisible = gamespace.IsTemplateVisible(vm.Name) }) .Where(s => s.IsVisible) .OrderBy(s => s.Name) .ToArray(); } if (preview || gamespace.HasStarted) { var spec = JsonSerializer.Deserialize <ChallengeSpec>(gamespace.Challenge, jsonOptions); if (spec.Challenge == null) { return(state); } // TODO: get active question set // map challenge to safe model state.Challenge = MapChallenge(spec, 0); } return(state); }
private async Task Deploy(TopoMojo.Api.Data.Gamespace gamespace, bool sudo = false) { var tasks = new List <Task <Vm> >(); var spec = JsonSerializer.Deserialize <ChallengeSpec>(gamespace.Challenge ?? "{}", jsonOptions); var isoTargets = (spec.Challenge?.Iso?.Targets ?? "") .ToLower() .Split(' ', StringSplitOptions.RemoveEmptyEntries); var templates = Mapper.Map <List <ConvergedTemplate> >( gamespace.Workspace.Templates .Where(t => t.Variant == 0 || (t.Variant - 1) == gamespace.Variant) ); foreach (var template in templates.ToList()) { // normalize name for variant-specific templates if (template.Variant > 0 && template.Name.EndsWith($"_v{template.Variant}")) { template.Name = template.Name.Substring(0, template.Name.LastIndexOf('_')); } // apply template macro substitutions foreach (var kvp in spec.Transforms) { template.Guestinfo = template.Guestinfo?.Replace($"##{kvp.Key}##", kvp.Value); template.Detail = template.Detail?.Replace($"##{kvp.Key}##", kvp.Value); } // apply challenge iso if (string.IsNullOrEmpty(template.Iso) && isoTargets.Contains(template.Name.ToLower())) { template.Iso = $"{spec.Challenge.Iso.File}"; } // expand replicas int replicas = template.Replicas < 0 ? gamespace.PlayerCount : Math.Min(template.Replicas, _options.ReplicaLimit) ; if (replicas > 1) { for (int i = 1; i < replicas; i++) { var tt = template.Clone <ConvergedTemplate>(); tt.Name += $"_{i+1}"; templates.Add(tt); } template.Name += "_1"; } } foreach (var template in templates) { tasks.Add( _pod.Deploy( template .ToVirtualTemplate(gamespace.Id) .SetHostAffinity(gamespace.Workspace.HostAffinity), sudo ) ); } await Task.WhenAll(tasks.ToArray()); if (gamespace.Workspace.HostAffinity) { var vms = tasks.Select(t => t.Result).ToArray(); await _pod.SetAffinity(gamespace.Id, vms, true); foreach (var vm in vms) { vm.State = VmPowerState.Running; } } if (gamespace.StartTime.Year <= 1) { gamespace.StartTime = DateTimeOffset.UtcNow; await _store.Update(gamespace); } }