private async Task DownloadImageAsync(IDockerClient dockerClient, VersionReference versionReference,
                                              CancellationToken cancellationToken)
        {
            var versionRequest = new GetImageDownloadInfoRequest()
            {
                Id = versionReference.Id
            };

            Logger.Information("Getting application download information for version {ImageId}...", versionReference.ImageId);

            //Get the download info
            var downloadInfo =
                await _deviceApiClient.ApplicationDownloadInfo.GetApplicationVersionDownloadInfo(versionRequest,
                                                                                                 cancellationToken);

            string fromImage = $"{downloadInfo.Registry}/{downloadInfo.Repository}:{downloadInfo.Name}";

            //Dowlnoad it!
            Logger.Information("Downloading with fromImage = '{FromImage}'...", fromImage);

            var imageCreateParameters = new ImagesCreateParameters
            {
                FromImage = fromImage
            };

            var authConfig = new AuthConfig()
            {
            };

            //Do the donwload!!!!!
            await dockerClient.Images.CreateImageAsync(
                imageCreateParameters,
                authConfig,
                new Progress <JSONMessage>(m => Console.WriteLine($"\tCreateImageProgress: {m.ProgressMessage}")),
                cancellationToken);

            Logger.Information("Application image {ImageId} downloaded.", versionReference.ImageId);
        }
        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);
        }
Esempio n. 3
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);
        }