Module: VagrantDockerNetworksManager::Util

Defined in:
lib/vagrant-docker-networks-manager/util.rb

Class Method Summary collapse

Class Method Details

.cidr_overlap?(a, b) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
126
127
128
129
130
131
# File 'lib/vagrant-docker-networks-manager/util.rb', line 123

def cidr_overlap?(a, b)
  ip_a, mask_a = a.to_s.split("/", 2); ip_b, mask_b = b.to_s.split("/", 2)
  return false unless mask_a && mask_b
  na = IPAddr.new(ip_a).mask(mask_a.to_i)
  nb = IPAddr.new(ip_b).mask(mask_b.to_i)
  na.include?(IPAddr.new(ip_b)) || nb.include?(IPAddr.new(ip_a))
rescue
  false
end

.docker_available?Boolean

Returns:

  • (Boolean)


22
23
24
25
26
27
# File 'lib/vagrant-docker-networks-manager/util.rb', line 22

def docker_available?
  _out, _err, status = Open3.capture3("docker", "info")
  status.success?
rescue
  false
end

.docker_network_exists?(name) ⇒ Boolean

Returns:

  • (Boolean)


29
30
31
32
# File 'lib/vagrant-docker-networks-manager/util.rb', line 29

def docker_network_exists?(name)
  out, _err, st = Open3.capture3("docker", "network", "ls", "--format", "{{.Name}}")
  st.success? && out.split.include?(name)
end

.docker_subnet_conflicts?(target_cidr, ignore_network: nil) ⇒ Boolean

Returns:

  • (Boolean)


146
147
148
149
150
# File 'lib/vagrant-docker-networks-manager/util.rb', line 146

def docker_subnet_conflicts?(target_cidr, ignore_network: nil)
  t_norm = normalize_cidr(target_cidr)
  return false unless t_norm
  each_docker_cidr(ignore_network: ignore_network).any? { |c| cidr_overlap?(t_norm, c) }
end

.each_docker_cidr(ignore_network: nil) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/vagrant-docker-networks-manager/util.rb', line 133

def each_docker_cidr(ignore_network: nil)
  out, _e, st = Open3.capture3("docker", "network", "ls", "-q")
  return [] unless st.success?
  out.split.each_slice(50).flat_map do |chunk|
    o, _e2, st2 = Open3.capture3("docker", "network", "inspect", *chunk)
    next [] unless st2.success?
    JSON.parse(o).filter_map do |net|
      next if ignore_network && net["Name"] == ignore_network
      (net.dig("IPAM","Config") || []).map { |cfg| normalize_cidr(cfg["Subnet"]) }.compact
    end.flatten
  end
end

.inspect_networks_batched(ids_or_names) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/vagrant-docker-networks-manager/util.rb', line 40

def inspect_networks_batched(ids_or_names)
  result = {}
  ids_or_names.each_slice(50) do |chunk|
    out, _e, st = Open3.capture3("docker", "network", "inspect", *chunk)
    next unless st.success?
    JSON.parse(out).each do |net|
      subs = (net.dig("IPAM","Config") || []).map { |c| c["Subnet"] }.compact
      cons = (net["Containers"] || {}).size
      key  = net["Id"] || net["Name"]
      result[key] = { subnets: subs, containers_count: cons }
      result[net["Name"]] ||= result[key]
    end
  end
  result
rescue
  {}
end

.list_plugin_networksObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/vagrant-docker-networks-manager/util.rb', line 58

def list_plugin_networks
  out, _err, st = Open3.capture3(
    "docker", "network", "ls",
    "--filter", "label=com.vagrant.plugin=docker_networks_manager",
    "--format", "{{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.Scope}}"
  )
  return [] unless st.success?

  rows = out.lines.map do |line|
    id, name, driver, scope = line.strip.split("\t", 4)
    { id: id, name: name, driver: driver, scope: scope }
  end

  details = inspect_networks_batched(rows.map { |r| r[:name] })
  rows.each do |r|
    subs = details.dig(r[:name], :subnets) || []
    r[:subnets] = subs.empty? ? "-" : subs.join(", ")
  end

  rows
rescue
  []
end

.list_plugin_networks_detailedObject



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/vagrant-docker-networks-manager/util.rb', line 82

def list_plugin_networks_detailed
  out, _err, st = Open3.capture3(
    "docker", "network", "ls",
    "--filter", "label=com.vagrant.plugin=docker_networks_manager",
    "--format", "{{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.Scope}}"
  )
  return [] unless st.success?

  rows = out.lines.map { |line|
    id, name, driver, scope = line.strip.split("\t", 4)
    { id: id, name: name, driver: driver, scope: scope }
  }

  details = inspect_networks_batched(rows.map { |r| r[:name] })
  rows.each do |r|
    r[:subnets]    = details.dig(r[:name], :subnets) || []
    r[:containers] = details.dig(r[:name], :containers_count) || 0
  end
  rows
end

.normalize_cidr(cidr) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/vagrant-docker-networks-manager/util.rb', line 113

def normalize_cidr(cidr)
  ip_str, mask_str = cidr.to_s.split("/", 2)
  return nil unless ip_str && mask_str && mask_str =~ /^\d+$/ && (0..32).include?(mask_str.to_i)
  ip = IPAddr.new(ip_str) rescue nil
  return nil unless ip&.ipv4?
  "#{ip.mask(mask_str.to_i)}/#{mask_str.to_i}"
rescue
  nil
end

.read_network_labels(name) ⇒ Object



34
35
36
37
38
# File 'lib/vagrant-docker-networks-manager/util.rb', line 34

def read_network_labels(name)
  out, _err, st = Open3.capture3("docker", "network", "inspect", name, "--format", "{{json .Labels}}")
  return {} unless st.success?
  JSON.parse(out.to_s.strip) rescue {}
end

.sh!(*args) ⇒ Object



12
13
14
15
16
17
18
19
20
# File 'lib/vagrant-docker-networks-manager/util.rb', line 12

def sh!(*args)
  if ENV["VDNM_VERBOSE"] == "1"
    printable = ["docker", *args].map(&:to_s).shelljoin
    $stderr.puts("[VDNM] #{printable}")
    system("docker", *args)
  else
    system("docker", *args, out: File::NULL, err: :out)
  end
end

.valid_subnet?(cidr) ⇒ Boolean

Returns:

  • (Boolean)


103
104
105
106
107
108
109
110
111
# File 'lib/vagrant-docker-networks-manager/util.rb', line 103

def valid_subnet?(cidr)
  ip_str, mask_str = cidr.to_s.split("/", 2)
  return false unless ip_str && mask_str && mask_str =~ /^\d+$/ && (0..32).include?(mask_str.to_i)
  ip = IPAddr.new(ip_str) rescue nil
  return false unless ip && ip.ipv4?
  (ip.mask(mask_str.to_i).to_s == ip_str)
rescue
  false
end