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); }
/// <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); }