// Build image dependency tree public DockerImageNode BuildImageTree(IList <ImageInspectResponse> images, IList <ContainerListResponse> containerListResponses, IList <ContainerInspectResponse> containerInspectResponses, ImageInspectResponse image) { var containerList = containerListResponses.Where(r => string.Equals(r.ImageID, image.ID)).ToList(); IList <ContainerInspectResponse> containers = new List <ContainerInspectResponse>(); foreach (var container in containerList) { containers.Add(containerInspectResponses.Where(c => string.Equals(c.ID, container.ID)).FirstOrDefault()); } var dockerImageNode = new DockerImageNode() { InspectResponse = image, Children = new List <DockerImageNode>(), Containers = containers, }; var children = images.Where(i => string.Equals(i.Parent, image.ID)).ToList(); foreach (var child in children) { var childNode = BuildImageTree(images, containerListResponses, containerInspectResponses, child); childNode.Parent = dockerImageNode; dockerImageNode.Children.Add(childNode); } return(dockerImageNode); }
/// <summary> /// TODO /// </summary> /// <param name="imageID"></param> /// <returns></returns> private async Task <string> GetImageVersionAsync(string imageID) { if (this._imageVersionMap.TryGetValue(imageID, out string versionString)) { return(versionString); } ImageInspectResponse imageInfo = await this._dockerClient.Images.InspectImageAsync(imageID); string version = imageInfo.Config.Env.First(env => env.StartsWith("VERSION")).Split("=")[1]; if (!this._imageVersionMap.ContainsKey(imageID)) { this._imageVersionMap.TryAdd(imageID, version); } return(version); }
public PlcOpcUaServer() { Uri dockerUri = null; try { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { dockerUri = new Uri("tcp://localhost:2375"); } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { dockerUri = new Uri("unix:///var/run/docker.sock"); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { dockerUri = new Uri("not supported"); } _dockerClient = new DockerClientConfiguration(dockerUri).CreateClient(); } catch { throw new Exception($"Please adjust your docker deamon endpoint '{dockerUri}' for your configuration."); } // cleanup all PLC containers CleanupContainerAsync().Wait(); // pull the latest image ImagesCreateParameters createParameters = new ImagesCreateParameters(); createParameters.FromImage = _plcImage; createParameters.Tag = "latest"; try { _dockerClient.Images.CreateImageAsync(createParameters, new AuthConfig(), new Progress <JSONMessage>()).Wait(); } catch (Exception) { throw new Exception($"Cannot pull image '{_plcImage}"); } ImageInspectResponse imageInspectResponse = _dockerClient.Images.InspectImageAsync(_plcImage).Result; // create a new container CreateContainerParameters containerParams = new CreateContainerParameters(); containerParams.Image = _plcImage; containerParams.Hostname = "opcplc"; containerParams.Name = "opcplc"; containerParams.Cmd = new string[] { "--aa", "--pn", $"{_plcPort}" }; // workaround .NET2.1 issue for private key access if (imageInspectResponse.Os.Equals("windows", StringComparison.InvariantCultureIgnoreCase)) { containerParams.Cmd.Add("--at"); containerParams.Cmd.Add("X509Store"); } containerParams.ExposedPorts = new Dictionary <string, EmptyStruct>(); containerParams.ExposedPorts.Add(new KeyValuePair <string, EmptyStruct>($"{_plcPort}/tcp", new EmptyStruct())); containerParams.HostConfig = new HostConfig(); PortBinding portBinding = new PortBinding(); portBinding.HostPort = _plcPort; portBinding.HostIP = null; List <PortBinding> portBindings = new List <PortBinding>(); portBindings.Add(portBinding); containerParams.HostConfig.PortBindings = new Dictionary <string, IList <PortBinding> >(); containerParams.HostConfig.PortBindings.Add($"{_plcPort}/tcp", portBindings); CreateContainerResponse response = null; try { response = _dockerClient.Containers.CreateContainerAsync(containerParams).Result; _plcContainerId = response.ID; } catch (Exception) { throw; } try { _dockerClient.Containers.StartContainerAsync(_plcContainerId, new ContainerStartParameters()).Wait(); } catch (Exception) { throw; } }
/// <summary> /// Pull and verify a new docker image and update the docker-compose file /// </summary> /// <param name="act">The action containing the new chainspec url and checksum</param> /// <param name="expectedState">The expected state that the action was derived from</param> /// <exception cref="UpdateVerificationException"> /// Thrown when update is not able to be verified. Reasons: /// <list type="bullet"> /// <item>The image is not able to be pulled</item> /// <item>Checksum of the downloaded content doesn't match</item> /// </list> /// </exception> public void UpdateDocker(StateChangeAction act, NodeState expectedState) { if (act.Mode != UpdateMode.Docker) { throw new UpdateVerificationException("Action with wrong update mode passed"); } if (string.IsNullOrWhiteSpace(act.Payload) || string.IsNullOrWhiteSpace(act.PayloadHash)) { throw new UpdateVerificationException("Payload or hash are empty"); } if (act.Payload != expectedState.DockerImage || act.PayloadHash != expectedState.DockerChecksum) { throw new UpdateVerificationException("Action vs. node state mismatch"); } Log($"Pulling new parity image [{act.Payload}] .."); // Prepare progress logging stub Progress <JSONMessage> progress = new Progress <JSONMessage>(); try { // pull docker image _dcc.PullImage(new ImagesCreateParameters { FromImage = act.Payload.Split(':')[0], Tag = act.Payload.Split(':')[1] }, null, progress); } catch (Exception e) { throw new UpdateVerificationException("Unable to pull new image.", e); } // verify docker image id against expected hash ImageInspectResponse inspectResult = _dcc.InspectImage(act.Payload); string dockerHash = inspectResult.ID.Replace("sha256:", string.Empty); if (dockerHash != act.PayloadHash) { Log("Image hashes don't match. Cancel update."); _dcc.DeleteImage(act.Payload); Log("Pulled imaged removed."); throw new UpdateVerificationException("Docker image hashes don't match."); } // Image is legit. update docker compose Log("Image valid. Updating stack."); NodeState stateBeforeUpgrade = _configProvider.ReadCurrentState(); // Write new config file and try to upgrade _configProvider.WriteNewState(expectedState); // restart/upgrade stack try { _dcc.ApplyChangesToStack(_stackPath, false); } catch { // Stack upgrade didn't work - rollback to last config file and rethrow _configProvider.WriteNewState(stateBeforeUpgrade); throw; } }
public async Task MonitorEventsFiltered_Succeeds() { string newTag = $"MonitorTests-{Guid.NewGuid().ToString().Substring(1, 10)}"; string newImageRespositoryName = Guid.NewGuid().ToString(); await _client.Images.TagImageAsync( $"{_repositoryName}:{_tag}", new ImageTagParameters { RepositoryName = newImageRespositoryName, Tag = newTag }, _cts.Token ); ImageInspectResponse image = await _client.Images.InspectImageAsync( $"{newImageRespositoryName}:{newTag}", _cts.Token ); var progressCalledCounter = 0; var eventsParams = new ContainerEventsParameters() { Filters = new Dictionary <string, IDictionary <string, bool> >() { { "event", new Dictionary <string, bool>() { { "tag", true }, { "untag", true } } }, { "type", new Dictionary <string, bool>() { { "image", true } } }, { "image", new Dictionary <string, bool>() { { image.ID, true } } } } }; var progress = new Progress <Message>((m) => { progressCalledCounter++; Assert.True(m.Status == "tag" || m.Status == "untag"); _output.WriteLine($"MonitorEventsFiltered_Succeeds: Message received: {m.Action} - {m.Status} {m.From} - {m.Type}"); }); using var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token); var task = Task.Run(() => _client.System.MonitorEventsAsync(eventsParams, progress, cts.Token)); await _client.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = $"{_repositoryName}:{_tag}" }, null, new Progress <JSONMessage>()); await _client.Images.TagImageAsync($"{_repositoryName}:{_tag}", new ImageTagParameters { RepositoryName = _repositoryName, Tag = newTag }); await _client.Images.DeleteImageAsync($"{_repositoryName}:{newTag}", new ImageDeleteParameters()); var createContainerResponse = await _client.Containers.CreateContainerAsync(new CreateContainerParameters { Image = $"{_repositoryName}:{_tag}" }); await _client.Containers.RemoveContainerAsync(createContainerResponse.ID, new ContainerRemoveParameters(), cts.Token); await Task.Delay(TimeSpan.FromSeconds(1)); cts.Cancel(); await Assert.ThrowsAsync <TaskCanceledException>(() => task); Assert.Equal(2, progressCalledCounter); Assert.True(task.IsCanceled); }