diff --git a/lib/facter/docker.rb b/lib/facter/docker.rb index f01749fb..a8d33ad6 100644 --- a/lib/facter/docker.rb +++ b/lib/facter/docker.rb @@ -41,6 +41,16 @@ def interfaces Facter.value(:interfaces).split(',') end +Facter.add(:docker_version) do + confine { Facter::Core::Execution.which('docker') } + setcode do + value = Facter::Core::Execution.execute( + "#{docker_command} version --format '{{json .}}'", timeout: 90 + ) + JSON.parse(value) + end +end + Facter.add(:docker_client_version) do setcode do docker_version = Facter.value(:docker_version) @@ -65,70 +75,77 @@ def interfaces end end -Facter.add(:docker_version) do - setcode do - if Facter::Core::Execution.which('docker') - value = Facter::Core::Execution.execute( - "#{docker_command} version --format '{{json .}}'", timeout: 90 - ) - val = JSON.parse(value) - end - val - end -end - Facter.add(:docker_worker_join_token) do + confine { Facter::Core::Execution.which('docker') } setcode do - if Facter::Core::Execution.which('docker') - val = Facter::Core::Execution.execute( - "#{docker_command} swarm join-token worker -q", timeout: 90 - ) + # only run `docker swarm` commands if this node is in active in a cluster + docker_json_str = Facter::Core::Execution.execute( + "#{docker_command} info --format '{{json .}}'", timeout: 90 + ) + begin + docker = JSON.parse(docker_json_str) + if docker.fetch('Swarm', {})['LocalNodeState'] == 'active' + val = Facter::Core::Execution.execute( + "#{docker_command} swarm join-token worker -q", timeout: 90 + ) + end + rescue JSON::ParserError + nil end val end end Facter.add(:docker_manager_join_token) do + confine { Facter::Core::Execution.which('docker') } setcode do - if Facter::Core::Execution.which('docker') - val = Facter::Core::Execution.execute( - "#{docker_command} swarm join-token manager -q", timeout: 90 - ) + # only run `docker swarm` commands if this node is in active in a cluster + docker_json_str = Facter::Core::Execution.execute( + "#{docker_command} info --format '{{json .}}'", timeout: 90 + ) + begin + docker = JSON.parse(docker_json_str) + if docker.fetch('Swarm', {})['LocalNodeState'] == 'active' + val = Facter::Core::Execution.execute( + "#{docker_command} swarm join-token manager -q", timeout: 90 + ) + end + rescue JSON::ParserError + nil end val end end Facter.add(:docker) do + confine { Facter::Core::Execution.which('docker') } setcode do docker_version = Facter.value(:docker_client_version) if docker_version&.match?(%r{1[0-9][0-2]?[.]\w+}) - if Facter::Core::Execution.which('docker') - docker_json_str = Facter::Core::Execution.execute( - "#{docker_command} info --format '{{json .}}'", timeout: 90 - ) - begin - docker = JSON.parse(docker_json_str) - docker['network'] = {} + docker_json_str = Facter::Core::Execution.execute( + "#{docker_command} info --format '{{json .}}'", timeout: 90 + ) + begin + docker = JSON.parse(docker_json_str) + docker['network'] = {} - docker['network']['managed_interfaces'] = {} - network_list = Facter::Core::Execution.execute("#{docker_command} network ls | tail -n +2", timeout: 90) - docker_network_names = [] - network_list.each_line { |line| docker_network_names.push line.split[1] } - docker_network_ids = [] - network_list.each_line { |line| docker_network_ids.push line.split[0] } - docker_network_names.each do |network| - inspect = JSON.parse(Facter::Core::Execution.execute("#{docker_command} network inspect #{network}", timeout: 90)) - docker['network'][network] = inspect[0] - network_id = docker['network'][network]['Id'][0..11] - interfaces.each do |iface| - docker['network']['managed_interfaces'][iface] = network if %r{#{network_id}}.match?(iface) - end + docker['network']['managed_interfaces'] = {} + network_list = Facter::Core::Execution.execute("#{docker_command} network ls | tail -n +2", timeout: 90) + docker_network_names = [] + network_list.each_line { |line| docker_network_names.push line.split[1] } + docker_network_ids = [] + network_list.each_line { |line| docker_network_ids.push line.split[0] } + docker_network_names.each do |network| + inspect = JSON.parse(Facter::Core::Execution.execute("#{docker_command} network inspect #{network}", timeout: 90)) + docker['network'][network] = inspect[0] + network_id = docker['network'][network]['Id'][0..11] + interfaces.each do |iface| + docker['network']['managed_interfaces'][iface] = network if %r{#{network_id}}.match?(iface) end - docker - rescue JSON::ParserError - nil end + docker + rescue JSON::ParserError + nil end end end diff --git a/spec/fixtures/facts/docker_info b/spec/fixtures/facts/docker_info index e225760b..f023ab01 100644 --- a/spec/fixtures/facts/docker_info +++ b/spec/fixtures/facts/docker_info @@ -131,7 +131,7 @@ }, "ControlAvailable": false, "Error": "", - "LocalNodeState": "inactive", + "LocalNodeState": "active", "Managers": 0, "NodeAddr": "", "NodeID": "", diff --git a/spec/fixtures/facts/docker_info_swarm_inactive b/spec/fixtures/facts/docker_info_swarm_inactive new file mode 100644 index 00000000..e225760b --- /dev/null +++ b/spec/fixtures/facts/docker_info_swarm_inactive @@ -0,0 +1,143 @@ +{ + "Architecture": "x86_64", + "BridgeNfIp6tables": true, + "BridgeNfIptables": true, + "CPUSet": true, + "CPUShares": true, + "CgroupDriver": "cgroupfs", + "ClusterAdvertise": "", + "ClusterStore": "", + "ContainerdCommit": { + "Expected": "4ab9917febca54791c5f071a9d1f404867857fcc", + "ID": "4ab9917febca54791c5f071a9d1f404867857fcc" + }, + "Containers": 46, + "ContainersPaused": 0, + "ContainersRunning": 5, + "ContainersStopped": 41, + "CpuCfsPeriod": true, + "CpuCfsQuota": true, + "Debug": false, + "DefaultRuntime": "runc", + "DockerRootDir": "/var/lib/docker", + "Driver": "aufs", + "DriverStatus": [ + [ + "Root Dir", + "/var/lib/docker/aufs" + ], + [ + "Backing Filesystem", + "extfs" + ], + [ + "Dirs", + "408" + ], + [ + "Dirperm1 Supported", + "false" + ] + ], + "ExperimentalBuild": false, + "HttpProxy": "", + "HttpsProxy": "", + "ID": "VYL2:ZOEC:PG3V:3UFK:EXBT:FR3X:6IY4:ELX4:EQ5B:35C7:6OPZ:EQC6", + "IPv4Forwarding": true, + "Images": 50, + "IndexServerAddress": "https://index.docker.io/v1/", + "InitBinary": "docker-init", + "InitCommit": { + "Expected": "949e6fa", + "ID": "949e6fa" + }, + "Isolation": "", + "KernelMemory": true, + "KernelVersion": "3.13.0-115-generic", + "Labels": null, + "LiveRestoreEnabled": false, + "LoggingDriver": "json-file", + "MemTotal": 1977839616, + "MemoryLimit": true, + "NCPU": 2, + "NEventsListener": 0, + "NFd": 52, + "NGoroutines": 50, + "Name": "docker00", + "NoProxy": "", + "OSType": "linux", + "OomKillDisable": true, + "OperatingSystem": "Ubuntu 14.04.5 LTS", + "Plugins": { + "Authorization": null, + "Network": [ + "bridge", + "host", + "macvlan", + "null", + "overlay" + ], + "Volume": [ + "local" + ] + }, + "RegistryConfig": { + "IndexConfigs": { + "docker.io": { + "Mirrors": null, + "Name": "docker.io", + "Official": true, + "Secure": true + } + }, + "InsecureRegistryCIDRs": [ + "127.0.0.0/8" + ], + "Mirrors": [] + }, + "RuncCommit": { + "Expected": "54296cf40ad8143b62dbcaa1d90e520a2136ddfe", + "ID": "54296cf40ad8143b62dbcaa1d90e520a2136ddfe" + }, + "Runtimes": { + "runc": { + "path": "docker-runc" + } + }, + "SecurityOptions": [ + "name=apparmor" + ], + "ServerVersion": "17.03.1-ce", + "SwapLimit": false, + "Swarm": { + "Cluster": { + "CreatedAt": "0001-01-01T00:00:00Z", + "ID": "", + "Spec": { + "CAConfig": {}, + "Dispatcher": {}, + "EncryptionConfig": { + "AutoLockManagers": false + }, + "Orchestration": {}, + "Raft": { + "ElectionTick": 0, + "HeartbeatTick": 0 + }, + "TaskDefaults": {} + }, + "UpdatedAt": "0001-01-01T00:00:00Z", + "Version": {} + }, + "ControlAvailable": false, + "Error": "", + "LocalNodeState": "inactive", + "Managers": 0, + "NodeAddr": "", + "NodeID": "", + "Nodes": 0, + "RemoteManagers": null + }, + "SystemStatus": null, + "SystemTime": "2017-04-11T01:12:52.292117616-04:00" +} diff --git a/spec/unit/lib/facter/docker_spec.rb b/spec/unit/lib/facter/docker_spec.rb index 88415dfe..7e2d600b 100644 --- a/spec/unit/lib/facter/docker_spec.rb +++ b/spec/unit/lib/facter/docker_spec.rb @@ -4,18 +4,24 @@ require 'json' describe 'Facter::Util::Fact' do + let(:docker_command) do + if Facter.value(:kernel) == 'windows' + 'powershell -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -c docker' + else + 'docker' + end + end + before :each do Facter.clear allow(Facter::Core::Execution).to receive(:which).and_call_original allow(Facter::Core::Execution).to receive(:execute).and_call_original if Facter.value(:kernel) == 'windows' - docker_command = 'powershell -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -c docker' allow(Facter::Core::Execution).to receive(:which).with('dhcpcd').and_return('C:\Windows\dhcpd.exe') allow(Facter::Core::Execution).to receive(:which).with('route').and_return('C:\Windows\System32\ROUTE.EXE') allow(Facter::Core::Execution).to receive(:which).with('docker').and_return('C:\Program Files\Docker\docker.exe') else - docker_command = 'docker' allow(Facter::Core::Execution).to receive(:which).with('route').and_return('/usr/bin/route') allow(Facter::Core::Execution).to receive(:which).with('dhcpcd').and_return('/usr/bin/dhcpd') allow(Facter::Core::Execution).to receive(:which).with('docker').and_return('/usr/bin/docker') @@ -129,4 +135,25 @@ ) end end + + describe 'docker swarm worker join-token with inactive swarm cluster' do + before :each do + docker_info = File.read(fixtures('facts', 'docker_info_swarm_inactive')) + allow(Facter::Core::Execution).to receive(:execute).with("#{docker_command} info --format '{{json .}}'", timeout: 90).and_return(docker_info) + end + it do + expect(Facter.fact(:docker_worker_join_token).value).to be_nil + end + end + + describe 'docker swarm manager join-token with inactive swarm cluster' do + before :each do + docker_info = File.read(fixtures('facts', 'docker_info_swarm_inactive')) + allow(Facter::Core::Execution).to receive(:execute).with("#{docker_command} info --format '{{json .}}'", timeout: 90).and_return(docker_info) + end + + it do + expect(Facter.fact(:docker_manager_join_token).value).to be_nil + end + end end