public IActionResult Get(Guid id)
        {
            using (var connection = _connectionFactory.CreateAndOpen())
            {
                //Get the device
                var device = connection.Get <Device>(id);

                if (device == null)
                {
                    return(NotFound());
                }

                var configuration = new DeviceConfiguration()
                {
                    DeviceKey    = device.DeviceKey,
                    DeviceId     = device.Id,
                    DeviceApiUrl = _provisioningConfig.DeviceApiUrl,
                    PollSeconds  = 60,
                };

                //Craft the response
                var response = new GetDeviceConfigurationResponse()
                {
                    DeviceConfiguration = JsonConvert.SerializeObject(configuration)
                };

                //Put together the configuration
                return(Ok(response));
            }
        }
Пример #2
0
        public async Task <bool> UpdateAsync(GetDeviceConfigurationResponse configuration, CancellationToken cancellationToken)
        {
            try
            {
                return(await UpdateCoreAsync(configuration, cancellationToken));
            }
            catch (Exception ex)
            {
                Logger.Warning(ex, "An error occurred during update.");
            }

            return(false);
        }
Пример #3
0
        public IActionResult Get()
        {
            using (var connection = _connectionFactory.CreateAndOpen())
            {
                //TODO: Consider executing a single command with multiple result sets.
                //      Might have to use some T-SQL to get the application given the device id.

                //Get the device
                var device = connection.Get <Device>(DeviceId);

                if (device == null)
                {
                    return(NotFound("Unable to find device."));
                }

                var application = connection.Get <Application>(device.ApplicationId);

                if (application == null)
                {
                    return(NotFound("Unable to find application."));
                }

                //Get the environment variables
                var deviceEnvironmentVariables      = connection.GetDeviceEnvironmentVariables(DeviceId);
                var applicationEnvironmentVariables = connection.GetApplicationEnvironmentVariables(application.Id);

                var applicationVersionId = application.ApplicationVersionId;
                var agentVersionId       = application.AgentVersionId;

                //Start out with the version information at the application level.
                var response = new GetDeviceConfigurationResponse
                {
                    RootFileSystemVersionId = application.RootFileSystemVersionId,
                    ConfigurationVersion    = device.ConfigurationVersion
                };

                //Override with device level version information (if available)
                if (device.AgentVersionId != null)
                {
                    agentVersionId = device.AgentVersionId.Value;
                }

                if (device.ApplicationVersionId != null)
                {
                    applicationVersionId = device.ApplicationVersionId.Value;
                }

                if (device.RootFileSystemVersionId != null)
                {
                    response.RootFileSystemVersionId = device.RootFileSystemVersionId.Value;
                }

                //Get the detailed application version information
                if (applicationVersionId != null)
                {
                    var applicationVersion = connection.Get <ApplicationVersion>(applicationVersionId.Value);

                    if (applicationVersion == null)
                    {
                        return(StatusCode(StatusCodes.Status500InternalServerError));
                    }

                    //Set thhe image reference
                    response.ApplicationVersion = new VersionReference
                    {
                        Id      = applicationVersion.Id,
                        ImageId = applicationVersion.ImageId,
                        Name    = applicationVersion.Name
                    };
                }

                //Get the detailed agent version id
                if (agentVersionId != null)
                {
                    var agentVersion = connection.Get <AgentVersion>(agentVersionId);

                    if (agentVersion == null)
                    {
                        return(StatusCode(StatusCodes.Status500InternalServerError));
                    }

                    response.AgentVersion = new VersionReference
                    {
                        Id      = agentVersion.Id,
                        ImageId = agentVersion.ImageId,
                        Name    = agentVersion.Name
                    };
                }

                //Once again, start out with the application level (this time environment variables)
                var effectiveEnvironmentVariables = new Dictionary <string, string>();

                foreach (var variable in applicationEnvironmentVariables)
                {
                    effectiveEnvironmentVariables[variable.Name] = variable.Value;
                }

                //Now add / override with the device level environment variables.
                foreach (var variable in deviceEnvironmentVariables)
                {
                    effectiveEnvironmentVariables[variable.Name] = variable.Value;
                }

                //Copy the effective variables to the response
                response.EnvironmentVariables = effectiveEnvironmentVariables.Select(v => new EnvironmentVariable
                {
                    Name  = v.Key,
                    Value = v.Value
                }).ToArray();

                //We're good
                return(Ok(response));
            }
        }
Пример #4
0
        public override async Task <bool> UpdateCoreAsync(GetDeviceConfigurationResponse configuration, CancellationToken cancellationToken)
        {
            //Get the current and next version
            string currentVersion = await GetCurrentVersionAsync(cancellationToken);

            VersionReference nextVersion = configuration.ApplicationVersion;

            //Shouldn't ever happen
            if (configuration.EnvironmentVariables == null)
            {
                configuration.EnvironmentVariables = new EnvironmentVariable[] {};
            }

            Logger.Information("The new configuration has {EnvironmentVariableCount} environment variables.", configuration.EnvironmentVariables.Length);

            if (nextVersion == null)
            {
                Logger.Information("No application version information was specified.");
                return(false);
            }

            if (currentVersion != nextVersion.ImageId)
            {
                Logger.Information("The application version has changed from {CurrentVersion} to {NextVersion}.", currentVersion, nextVersion.ImageId);
            }
            else
            {
                Logger.Verbose("The application version has stayed {CurrentVersion}.", currentVersion);

                //Get the container
                var container = await _dockerClient.GetContainerByImageId(nextVersion.ImageId, cancellationToken);

                if (container != null)
                {
                    try
                    {
                        //Inspect the container to get its environment variables
                        var containerInspection = await _dockerClient.Containers.InspectContainerAsync(container.ID, cancellationToken);

                        var imageInspection = await _dockerClient.Images.InspectImageAsync(nextVersion.ImageId, cancellationToken);

                        EnvironmentVariable[] currentVariables = containerInspection.Config.Env
                                                                 .Select(EnvironnmentVariableParser.Parse)
                                                                 .ToArray();

                        var parsedInspectionVariables = imageInspection.Config.Env
                                                        .Select(EnvironnmentVariableParser.Parse)
                                                        .ToArray();

                        //Resolve the potentially new environment variables
                        EnvironmentVariable[] newVariables = EnvironmentVariableResolver.Resolve(parsedInspectionVariables,
                                                                                                 configuration.EnvironmentVariables);

                        //Create the environment comparer
                        var comparer = new EnvironmentVariableComparer(Logger);

                        if (comparer.AreSame(currentVariables, newVariables))
                        {
                            Logger.Verbose("The environment variables have stayed the same.");
                            return(false);
                        }

                        Logger.Information("The environment variables have changed.");
                    }
                    catch (Exception ex)
                    {
                        Logger.Warning(ex, "An error occured while checking environment variables. Skipping check: {Message}", ex.Message);
                    }
                }
            }

            //Make sure that the image is downloaded
            if (!await _dockerClient.DoesImageExistAsync(nextVersion.ImageId, cancellationToken))
            {
                //Download the application
                await DownloadImageAsync(_dockerClient, nextVersion, cancellationToken);
            }

            //Ditch the current applications
            await _dockerClient.ObliterateContainerAsync(DockerContainerNames.Application, Logger, cancellationToken);

            Logger.Information("Create the container for {ImageId} ...", nextVersion.ImageId);

            //Create the container
            var createContainerResponse = await _dockerContainerFactory.CreateContainerAsync(
                _dockerClient,
                nextVersion.ImageId,
                configuration.EnvironmentVariables,
                cancellationToken);

            if (createContainerResponse.Warnings != null && createContainerResponse.Warnings.Any())
            {
                string formattedWarnings = string.Join(",", createContainerResponse.Warnings);

                Logger.Warning("Warnings during container creation: {Warnings}", formattedWarnings);
            }

            Logger.Information("Container {ContainerId} created for application {Application}. Starting...", createContainerResponse.ID, nextVersion.ImageId);

            //Attempt to start the container
            var started = await _dockerClient.Containers.StartContainerAsync(
                createContainerResponse.ID,
                new ContainerStartParameters(),
                cancellationToken);

            //Check to see if the application started
            if (!started)
            {
                Logger.Warning("Warning: Application not started.");
            }

            //We never need to exit the agent for updating an application.
            return(false);
        }
Пример #5
0
 public abstract Task <bool> UpdateCoreAsync(GetDeviceConfigurationResponse configuration, CancellationToken cancellationToken);
Пример #6
0
        /// <summary>
        /// If true, the caller should exit
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async Task <bool> UpdateCoreAsync(GetDeviceConfigurationResponse configuration, CancellationToken cancellationToken)
        {
            string currentVersion = await GetCurrentVersionAsync(cancellationToken);

            VersionReference nextVersion = configuration.AgentVersion;

            if (nextVersion == null)
            {
                return(false);
            }

            if (currentVersion == nextVersion.ImageId)
            {
                return(false);
            }

            //We don't support updating on linux
            if (!_platformDetecter.IsLinux)
            {
                Logger.Warning("Agent update is only supported on Linux.");
                return(false);
            }


            //Get all of the containers
            var containers = await _dockerClient.Containers.ListContainersAsync(
                new ContainersListParameters()
            {
                All = true
            }, cancellationToken);

            var a = containers.FindByName(DockerContainerNames.AgentA);
            var b = containers.FindByName(DockerContainerNames.AgentB);

            if (a == null && b == null)
            {
                Logger.Fatal("Unable to find the agent container.");
                return(false);
            }

            if (a != null && b != null)
            {
                Logger.Fatal("Two agent containers found. Unable to continue update.");
                return(false);
            }

            string existingContainerName;
            string newContainerName;

            ContainerListResponse existingContainer;

            if (a != null)
            {
                existingContainerName = DockerContainerNames.AgentA;
                newContainerName      = DockerContainerNames.AgentB;
                existingContainer     = a;
            }
            else
            {
                existingContainerName = DockerContainerNames.AgentB;
                newContainerName      = DockerContainerNames.AgentA;
                existingContainer     = b;
            }

            Logger.Information("The existing agent container is {ExistingContainer}. The new one will be {NewContainer}.", existingContainerName, newContainerName);

            //Make sure that the image is downloaded
            if (!await _dockerClient.DoesImageExistAsync(nextVersion.ImageId, cancellationToken))
            {
                await DownloadImageAsync(_dockerClient, nextVersion, cancellationToken);
            }

            Logger.Information("Creating new agent container...");

            //Create the new updated container
            var createContainerResponse = await _dockerContainerFactory.CreateContainerForUpdateAsync(
                _dockerClient,
                nextVersion.ImageId,
                existingContainerName,
                newContainerName,
                cancellationToken);

            //Show the warnings
            if (createContainerResponse.Warnings != null && createContainerResponse.Warnings.Any())
            {
                string formattedWarnings = string.Join(",", createContainerResponse.Warnings);

                Logger.Warning("Warnings during container creation: {Warnings}", formattedWarnings);
            }

            Logger.Information("Container {ContainerId} created for agent {ImageId}. Starting new agent...", createContainerResponse.ID, nextVersion.ImageId);

            //This is our last chance to get the hell out before committing
            if (cancellationToken.IsCancellationRequested)
            {
                return(false);
            }

            //Attempt to start the container
            var started = await _dockerClient.Containers.StartContainerAsync(
                createContainerResponse.ID,
                new ContainerStartParameters(),
                new CancellationToken());

            //Check to see if the application started
            if (started)
            {
                //Commit container suicide (this should kill the container that we're in)
                await _dockerClient.Containers.RemoveContainerAsync(existingContainer.ID,
                                                                    new ContainerRemoveParameters()
                {
                    Force = true
                }, new CancellationToken());

                //We may never get here
                return(true);
            }

            Logger.Warning("Warning: New agent not started. Not entirely sure why.");
            return(false);
        }