Пример #1
0
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            //Get input
            HandleInput(gameTime);

            //Get time of first frame
            if (startTime == -1)
            {
                startTime = (int)gameTime.TotalGameTime.TotalSeconds;
            }

            //Countdown
            countdown = startTime + TrafficGame.Duration - (int)gameTime.TotalGameTime.TotalSeconds;
            countdownUI.Update(countdown);
            if (countdown == -3)
            {
                EndGame();
            }

            //Detect if in target lane
            int lane = Road.GetLane(player.Position.X, 0.35f);

            inTargetLane = lane == environment.road.GetHighlightAtPlayerPos();
            environment.road.SetHighlightStatus(lane);

            //Step forward time (slo-mo if player crashed or countdown finished)
            float timeScale = 1.0f;

            if (player.crashed || countdown <= 0)
            {
                timeScale = 0.25f;
            }
            world.Step((float)gameTime.ElapsedGameTime.TotalSeconds * timeScale);

            if (state == GameState.RUNNING)
            {
                if (!player.crashed)
                {
                    //Minimum of 1 point per frame.
                    //Score based on velocity, double points if in correct lane
                    //TODO: For low velocities, we may want less than 1 point per frame.
                    int deltaScore = (int)Math.Max(1, (player.Velocity.Y * gameTime.ElapsedGameTime.TotalSeconds));
                    if (inTargetLane)
                    {
                        deltaScore *= 2;
                    }
                    score += deltaScore;
                }
            }
            else if (state == GameState.STARTING)
            {
                Camera.main.Zoom = Math.Min(1, Camera.main.Zoom + 0.05f);      //Zoom out camera to normal position
                if (gameTime.TotalGameTime.TotalSeconds - stateChangeTime > 3) //Start game after 3 seconds
                {
                    state           = GameState.RUNNING;
                    stateChangeTime = gameTime.TotalGameTime.TotalSeconds;
                    player.Velocity = new Vector2(player.Velocity.X, adjustedSpeed);
                }
            }
            else if (state == GameState.RESTARTING)
            {
                Camera.main.Zoom = Math.Max(0, Camera.main.Zoom - 0.05f);      //Zoom camera in as a transition
                if (gameTime.TotalGameTime.TotalSeconds - stateChangeTime > 1) //Reset world after 1 second
                {
                    state           = GameState.STARTING;
                    stateChangeTime = gameTime.TotalGameTime.TotalSeconds;
                    environment.Reset();
                    trafficManager.Reset();
                    player.Reset();
                    score = 0;
                }
            }

            //Update game stuff
            trafficManager.Update(gameTime, player, state);
            environment.Update(gameTime, player);
            player.Update(gameTime, InputManager.LateralMovement);

            //Light & camera follow player
            Camera.main.Target = new Vector2(player.Position.X, player.Position.Y);
            Lighting.Position  = new Vector3(Lighting.Position.X, player.Position.Y + 15, Lighting.Position.Z);

            //Update GUI
            scoreUI.Update(gameTime, score);
            fpsUI.Update(gameTime);
            titleUI.Update(gameTime);
        }
Пример #2
0
        /// <summary>
        /// Verify that we can create HTTPS traffic manager rules for a
        /// site on the proxy port using a specific hostname and various
        /// path prefixes and then verify that that the traffic manager actually
        /// works  by spinning up a [vegomatic] based service to accept the traffic.
        /// </summary>
        /// <param name="testName">Simple name (without spaces) used to ensure that URIs cached for different tests won't conflict.</param>
        /// <param name="proxyPort">The inbound proxy port.</param>
        /// <param name="network">The proxy network.</param>
        /// <param name="trafficManager">The traffic manager.</param>
        /// <param name="useCache">Optionally enable caching and verify.</param>
        /// <param name="serviceName">Optionally specifies the backend service name prefix (defaults to <b>vegomatic</b>).</param>
        /// <returns>The tracking <see cref="Task"/>.</returns>
        private async Task TestHttpsPrefix(string testName, int proxyPort, string network, TrafficManager trafficManager, bool useCache = false, string serviceName = "vegomatic")
        {
            // Append a GUID to the test name to ensure that we won't
            // conflict with what any previous test runs may have loaded
            // into the cache.

            testName += "-" + Guid.NewGuid().ToString("D");

            // Verify that we can create an HTTP traffic manager rule for a
            // site on the proxy port using a specific hostname and then
            // verify that that the traffic manager actually works by spinning
            // up a [vegomatic] based service to accept the traffic.

            var manager  = hive.GetReachableManager();
            var hostname = testHostname;

            manager.Connect();

            // Allow self-signed certificates.

            var handler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
            };

            using (var client = new TestHttpClient(disableConnectionReuse: true, handler: handler, disposeHandler: true))
            {
                // Add the test certificate.

                hive.Certificate.Set("test-load-balancer", certificate);

                // Setup the client to query the [vegomatic] service through the
                // proxy without needing to configure a hive DNS entry.

                client.BaseAddress = new Uri($"https://{manager.PrivateAddress}:{proxyPort}/");
                client.DefaultRequestHeaders.Host = hostname;

                // Create the traffic manager rules, one without a path prefix and
                // some others, some with intersecting prefixes so we can verify
                // that the longest prefixes are matched first.
                //
                // Each rule's backend will be routed to a service whose name
                // will be constructed from [testName] plus the prefix with the
                // slashes replaced with dashes.  Each service will be configured
                // to return its name.

                var prefixes = new PrefixInfo[]
                {
                    new PrefixInfo("/", $"{serviceName}"),
                    new PrefixInfo("/foo/", $"{serviceName}-foo"),
                    new PrefixInfo("/foo/bar/", $"{serviceName}-foo-bar"),
                    new PrefixInfo("/foobar/", $"{serviceName}-foobar"),
                    new PrefixInfo("/bar/", $"{serviceName}-bar")
                };

                // Spin the services up first in parallel (for speed).  Each of
                // these service will respond to requests with its service name.

                var tasks = new List <Task>();

                foreach (var prefix in prefixes)
                {
                    tasks.Add(Task.Run(
                                  () =>
                    {
                        manager.SudoCommand($"docker service create --name {prefix.ServiceName} --network {network} --replicas 1 {vegomaticImage} test-server server-id={prefix.ServiceName}").EnsureSuccess();
                    }));
                }

                await NeonHelper.WaitAllAsync(tasks, TimeSpan.FromSeconds(30));

                // Create the traffic manager rules.

                foreach (var prefix in prefixes)
                {
                    var rule = new TrafficHttpRule()
                    {
                        Name         = prefix.ServiceName,
                        CheckExpect  = "status 200",
                        CheckSeconds = 1,
                    };

                    if (useCache)
                    {
                        rule.Cache = new TrafficHttpCache()
                        {
                            Enabled = true
                        };
                    }

                    var frontend = new TrafficHttpFrontend()
                    {
                        Host      = hostname,
                        ProxyPort = proxyPort,
                        CertName  = "test-load-balancer"
                    };

                    if (!string.IsNullOrEmpty(prefix.Path))
                    {
                        frontend.PathPrefix = prefix.Path;
                    }

                    rule.Frontends.Add(frontend);

                    rule.Backends.Add(
                        new TrafficHttpBackend()
                    {
                        Server = prefix.ServiceName,
                        Port   = 80
                    });

                    trafficManager.SetRule(rule, deferUpdate: true);
                }

                trafficManager.Update();

                // Wait for all of the services to report being ready.

                await NeonHelper.WaitForAsync(
                    async() =>
                {
                    foreach (var prefix in prefixes)
                    {
                        try
                        {
                            var response = await client.GetAsync(prefix.Path);

                            response.EnsureSuccessStatusCode();
                        }
                        catch
                        {
                            return(false);
                        }
                    }

                    return(true);
                },
                    timeout : TimeSpan.FromSeconds(60),
                    pollTime : TimeSpan.FromSeconds(1));

                // Give everything a chance to stablize.

                await Task.Delay(TimeSpan.FromSeconds(5));

                // Now verify that prefix rules route to the correct backend service.

                foreach (var prefix in prefixes)
                {
                    var response = await client.GetAsync($"{prefix.Path}{testName}?expires=60");

                    response.EnsureSuccessStatusCode();

                    var body = await response.Content.ReadAsStringAsync();

                    Assert.Equal(prefix.ServiceName, body.Trim());

                    if (useCache)
                    {
                        // Verify that the request routed through Varnish.

                        Assert.True(ViaVarnish(response));

                        // This is the first request using the globally unique [testName]
                        // so it should not be a cache hit.

                        Assert.False(CacheHit(response));
                    }
                }

                // If caching is enabled, perform the requests again to ensure that
                // we see cache hits.

                if (useCache)
                {
                    foreach (var prefix in prefixes)
                    {
                        // Request the item again and verify that it was a cache hit.

                        var response = await client.GetAsync($"{prefix.Path}{testName}?expires=60");

                        response.EnsureSuccessStatusCode();

                        var body = await response.Content.ReadAsStringAsync();

                        Assert.Equal(prefix.ServiceName, body.Trim());
                        Assert.True(CacheHit(response));
                    }
                }
            }
        }
Пример #3
0
        /// <inheritdoc/>
        public void Run(ModuleContext context)
        {
            TrafficManager trafficManager = null;
            bool           isPublic       = false;
            string         name           = null;
            string         ruleName       = null;
            bool           deferUpdate    = false;

            if (!context.ValidateArguments(context.Arguments, validModuleArgs))
            {
                context.Failed = true;
                return;
            }

            // Obtain common arguments.

            context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [state]");

            if (!context.Arguments.TryGetValue <string>("state", out var state))
            {
                state = "present";
            }

            state = state.ToLowerInvariant();

            if (context.HasErrors)
            {
                return;
            }

            context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [name]");

            if (!context.Arguments.TryGetValue <string>("name", out name))
            {
                throw new ArgumentException($"[name] module argument is required.");
            }

            switch (name)
            {
            case "private":

                trafficManager = HiveHelper.Hive.PrivateTraffic;
                isPublic       = false;
                break;

            case "public":

                trafficManager = HiveHelper.Hive.PublicTraffic;
                isPublic       = true;
                break;

            default:

                throw new ArgumentException($"[name={name}] is not a one of the valid traffic manager names: [private] or [public].");
            }

            if (state == "present" || state == "absent")
            {
                context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [rule_name]");

                if (!context.Arguments.TryGetValue <string>("rule_name", out ruleName))
                {
                    throw new ArgumentException($"[rule_name] module argument is required.");
                }

                if (!HiveDefinition.IsValidName(ruleName))
                {
                    throw new ArgumentException($"[rule_name={ruleName}] is not a valid traffic manager rule name.");
                }

                context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [defer_update]");

                if (!context.Arguments.TryGetValue <bool>("defer_update", out deferUpdate))
                {
                    deferUpdate = false;
                }
            }

            // We have the required arguments, so perform the operation.

            switch (state)
            {
            case "absent":

                context.WriteLine(AnsibleVerbosity.Trace, $"Check if rule [{ruleName}] exists.");

                if (trafficManager.GetRule(ruleName) != null)
                {
                    context.WriteLine(AnsibleVerbosity.Trace, $"Rule [{ruleName}] does exist.");
                    context.WriteLine(AnsibleVerbosity.Info, $"Deleting rule [{ruleName}].");

                    if (context.CheckMode)
                    {
                        context.WriteLine(AnsibleVerbosity.Info, $"Rule [{ruleName}] will be deleted when CHECK-MODE is disabled.");
                    }
                    else
                    {
                        trafficManager.RemoveRule(ruleName, deferUpdate: deferUpdate);
                        context.WriteLine(AnsibleVerbosity.Trace, $"Rule [{ruleName}] deleted.");
                        context.Changed = true;
                    }
                }
                else
                {
                    context.WriteLine(AnsibleVerbosity.Trace, $"Rule [{ruleName}] does not exist.");
                }
                break;

            case "present":

                context.WriteLine(AnsibleVerbosity.Trace, $"Parsing [rule]");

                if (!context.Arguments.TryGetValue <JObject>("rule", out var routeObject))
                {
                    throw new ArgumentException($"[rule] module argument is required when [state={state}].");
                }

                var ruleText = routeObject.ToString();

                context.WriteLine(AnsibleVerbosity.Trace, "Parsing rule");

                var newRule = TrafficRule.Parse(ruleText, strict: true);

                context.WriteLine(AnsibleVerbosity.Trace, "Rule parsed successfully");

                // Use the name argument if the deserialized rule doesn't
                // have a name.  This will make it easier on operators because
                // they won't need to specify the name twice.

                if (string.IsNullOrWhiteSpace(newRule.Name))
                {
                    newRule.Name = ruleName;
                }

                // Ensure that the name passed as an argument and the
                // name within the rule definition match.

                if (!string.Equals(ruleName, newRule.Name, StringComparison.InvariantCultureIgnoreCase))
                {
                    throw new ArgumentException($"The [rule_name={ruleName}] argument and the rule's [{nameof(TrafficRule.Name)}={newRule.Name}] property are not the same.");
                }

                context.WriteLine(AnsibleVerbosity.Trace, "Rule name matched.");

                // Validate the rule.

                context.WriteLine(AnsibleVerbosity.Trace, "Validating rule.");

                var proxySettings     = trafficManager.GetSettings();
                var validationContext = new TrafficValidationContext(name, proxySettings);

                // $hack(jeff.lill):
                //
                // This ensures that [proxySettings.Resolvers] is initialized with
                // the built-in Docker DNS resolver.

                proxySettings.Validate(validationContext);

                // Load the TLS certificates into the validation context so we'll
                // be able to verify that any referenced certificates mactually exist.

                // $todo(jeff.lill):
                //
                // This code assumes that the operator is currently logged in with
                // root Vault privileges.  We'll have to do something else for
                // non-root logins.
                //
                // One idea might be to save two versions of the certificates.
                // The primary certificate with private key in Vault and then
                // just the public certificate in Consul and then load just
                // the public ones here.
                //
                // A good time to make this change might be when we convert to
                // use the .NET X.509 certificate implementation.

                if (!context.Login.HasVaultRootCredentials)
                {
                    throw new ArgumentException("Access Denied: Root Vault credentials are required.");
                }

                context.WriteLine(AnsibleVerbosity.Trace, "Reading hive certificates.");

                using (var vault = HiveHelper.OpenVault(Program.HiveLogin.VaultCredentials.RootToken))
                {
                    // List the certificate key/names and then fetch each one
                    // to capture details like the expiration date and covered
                    // hostnames.

                    foreach (var certName in vault.ListAsync("neon-secret/cert").Result)
                    {
                        context.WriteLine(AnsibleVerbosity.Trace, $"Reading: {certName}");

                        var certificate = vault.ReadJsonAsync <TlsCertificate>(HiveHelper.GetVaultCertificateKey(certName)).Result;

                        validationContext.Certificates.Add(certName, certificate);
                    }
                }

                context.WriteLine(AnsibleVerbosity.Trace, $"[{validationContext.Certificates.Count}] hive certificates downloaded.");

                // Actually perform the rule validation.

                newRule.Validate(validationContext);

                if (validationContext.HasErrors)
                {
                    context.WriteLine(AnsibleVerbosity.Trace, $"[{validationContext.Errors.Count}] Route validation errors.");

                    foreach (var error in validationContext.Errors)
                    {
                        context.WriteLine(AnsibleVerbosity.Important, error);
                        context.WriteErrorLine(error);
                    }

                    context.Failed = true;
                    return;
                }

                context.WriteLine(AnsibleVerbosity.Trace, "Rule is valid.");

                // Try reading any existing rule with this name and then determine
                // whether the two versions of the rule are actually different.

                context.WriteLine(AnsibleVerbosity.Trace, $"Looking for existing rule [{ruleName}]");

                var existingRule = trafficManager.GetRule(ruleName);
                var changed      = false;

                if (existingRule != null)
                {
                    context.WriteLine(AnsibleVerbosity.Trace, $"Rule exists: checking for differences.");

                    // Normalize the new and existing rules so the JSON text comparision
                    // will work properly.

                    newRule.Normalize(isPublic);
                    existingRule.Normalize(isPublic);

                    changed = !NeonHelper.JsonEquals(newRule, existingRule);

                    if (changed)
                    {
                        context.WriteLine(AnsibleVerbosity.Trace, $"Rules are different.");
                    }
                    else
                    {
                        context.WriteLine(AnsibleVerbosity.Info, $"Rules are the same.  No need to update.");
                    }
                }
                else
                {
                    changed = true;
                    context.WriteLine(AnsibleVerbosity.Trace, $"Rule [name={ruleName}] does not exist.");
                }

                if (changed)
                {
                    if (context.CheckMode)
                    {
                        context.WriteLine(AnsibleVerbosity.Info, $"Rule [{ruleName}] will be updated when CHECK-MODE is disabled.");
                    }
                    else
                    {
                        context.WriteLine(AnsibleVerbosity.Trace, $"Writing rule [{ruleName}].");
                        trafficManager.SetRule(newRule);
                        context.WriteLine(AnsibleVerbosity.Info, $"Rule updated.");
                        context.Changed = !context.CheckMode;
                    }
                }
                break;

            case "update":

                trafficManager.Update();
                context.Changed = true;
                context.WriteLine(AnsibleVerbosity.Info, $"Update signalled.");
                break;

            case "purge":

                var purgeItems         = context.ParseStringArray("purge_list");
                var purgeCaseSensitive = context.ParseBool("purge_case_sensitive");

                if (!purgeCaseSensitive.HasValue)
                {
                    purgeCaseSensitive = false;
                }

                if (purgeItems.Count == 0)
                {
                    context.WriteLine(AnsibleVerbosity.Important, $"[purge_list] is missing or empty.");
                    break;
                }

                trafficManager.Purge(purgeItems.ToArray(), caseSensitive: purgeCaseSensitive.Value);

                context.Changed = true;
                context.WriteLine(AnsibleVerbosity.Info, $"Purge request submitted.");
                break;

            default:

                throw new ArgumentException($"[state={state}] is not one of the valid choices: [present], [absent], or [update].");
            }
        }