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)); } }
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); }
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)); } }
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); }
public abstract Task <bool> UpdateCoreAsync(GetDeviceConfigurationResponse configuration, CancellationToken cancellationToken);
/// <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); }