summaryrefslogtreecommitdiff
path: root/provider_base/lib/macros/nodes.rb
blob: 0c6668a0892458ba7d103ec4a669ab05d7a5dc78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# encoding: utf-8

##
## node related macros
##

module LeapCli
  module Macro

    #
    # the list of all the nodes
    #
    def nodes
      global.nodes
    end

    #
    # grab an environment appropriate provider
    #
    def provider
      global.env(@node.environment).provider
    end

    #
    # returns a list of nodes that match the same environment
    #
    # if @node.environment is not set, we return other nodes
    # where environment is not set.
    #
    def nodes_like_me
      nodes[:environment => @node.environment]
    end

    #
    # returns a list of nodes that match the location name
    # and environment of @node.
    #
    def nodes_near_me
      if @node['location'] && @node['location']['name']
        nodes_like_me['location.name' => @node.location.name]
      else
        nodes_like_me['location' => nil]
      end
    end

    #
    #
    # picks a node out from the node list in such a way that:
    #
    # (1) which nodes picked which nodes is saved in secrets.json
    # (2) when other nodes call this macro with the same node list, they are guaranteed to get a different node
    # (3) if all the nodes in the pick_node list have been picked, remaining nodes are distributed randomly.
    #
    # if the node_list is empty, an exception is raised.
    # if node_list size is 1, then that node is returned and nothing is
    # memorized via the secrets.json file.
    #
    # `label` is needed to distinguish between pools of nodes for different purposes.
    #
    # TODO: more evenly balance after all the nodes have been picked.
    #
    def pick_node(label, node_list)
      if node_list.any?
        if node_list.size == 1
          return node_list.values.first
        else
          secrets_key = "pick_node(:#{label},#{node_list.keys.sort.join(',')})"
          secrets_value = @manager.secrets.retrieve(secrets_key, @node.environment) || {}
          secrets_value[@node.name] ||= begin
            node_to_pick = nil
            node_list.each_node do |node|
              next if secrets_value.values.include?(node.name)
              node_to_pick = node.name
            end
            node_to_pick ||= secrets_value.values.shuffle.first # all picked already, so pick a random one.
            node_to_pick
          end
          picked_node_name = secrets_value[@node.name]
          @manager.secrets.set(secrets_key, secrets_value, @node.environment)
          return node_list[picked_node_name]
        end
      else
        raise ArgumentError.new('pick_node(node_list): node_list cannot be empty')
      end
    end

  end
end