cloud-mu 3.1.5 → 3.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +5 -1
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/tasks/main.yml +16 -0
- data/bin/mu-adopt +2 -1
- data/bin/mu-configure +16 -0
- data/bin/mu-node-manage +15 -16
- data/cloud-mu.gemspec +2 -2
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
- data/extras/clean-stock-amis +25 -19
- data/extras/image-generators/AWS/win2k12.yaml +2 -0
- data/extras/image-generators/AWS/win2k16.yaml +2 -0
- data/extras/image-generators/AWS/win2k19.yaml +2 -0
- data/modules/mommacat.ru +1 -1
- data/modules/mu.rb +6 -5
- data/modules/mu/adoption.rb +19 -4
- data/modules/mu/cleanup.rb +181 -293
- data/modules/mu/cloud.rb +58 -17
- data/modules/mu/clouds/aws.rb +36 -1
- data/modules/mu/clouds/aws/container_cluster.rb +30 -21
- data/modules/mu/clouds/aws/role.rb +1 -1
- data/modules/mu/clouds/aws/vpc.rb +5 -1
- data/modules/mu/clouds/azure.rb +10 -0
- data/modules/mu/clouds/cloudformation.rb +10 -0
- data/modules/mu/clouds/google.rb +18 -4
- data/modules/mu/clouds/google/bucket.rb +2 -2
- data/modules/mu/clouds/google/container_cluster.rb +10 -7
- data/modules/mu/clouds/google/database.rb +3 -3
- data/modules/mu/clouds/google/firewall_rule.rb +3 -3
- data/modules/mu/clouds/google/function.rb +3 -3
- data/modules/mu/clouds/google/loadbalancer.rb +4 -4
- data/modules/mu/clouds/google/role.rb +18 -9
- data/modules/mu/clouds/google/server.rb +16 -14
- data/modules/mu/clouds/google/server_pool.rb +4 -4
- data/modules/mu/clouds/google/user.rb +2 -2
- data/modules/mu/clouds/google/vpc.rb +9 -13
- data/modules/mu/config.rb +1 -1
- data/modules/mu/config/container_cluster.rb +5 -0
- data/modules/mu/config/doc_helpers.rb +1 -1
- data/modules/mu/config/ref.rb +12 -6
- data/modules/mu/config/schema_helpers.rb +8 -3
- data/modules/mu/config/server.rb +7 -0
- data/modules/mu/config/tail.rb +1 -0
- data/modules/mu/config/vpc.rb +15 -7
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +48 -48
- data/modules/mu/deploy.rb +1 -1
- data/modules/mu/groomer.rb +1 -1
- data/modules/mu/groomers/ansible.rb +69 -4
- data/modules/mu/groomers/chef.rb +48 -4
- data/modules/mu/master.rb +75 -3
- data/modules/mu/mommacat.rb +104 -855
- data/modules/mu/mommacat/naming.rb +28 -0
- data/modules/mu/mommacat/search.rb +463 -0
- data/modules/mu/mommacat/storage.rb +185 -183
- data/modules/tests/super_simple_bok.yml +1 -3
- metadata +8 -5
data/modules/mu/cloud.rb
CHANGED
@@ -49,7 +49,7 @@ module MU
|
|
49
49
|
generic_instance_methods = [:create, :notify, :mu_name, :cloud_id, :config]
|
50
50
|
|
51
51
|
# Class methods which the base of a cloud implementation must implement
|
52
|
-
generic_class_methods_toplevel = [:required_instance_methods, :myRegion, :listRegions, :listAZs, :hosted?, :hosted_config, :config_example, :writeDeploySecret, :listCredentials, :credConfig, :listInstanceTypes, :adminBucketName, :adminBucketUrl, :habitat]
|
52
|
+
generic_class_methods_toplevel = [:required_instance_methods, :myRegion, :listRegions, :listAZs, :hosted?, :hosted_config, :config_example, :writeDeploySecret, :listCredentials, :credConfig, :listInstanceTypes, :adminBucketName, :adminBucketUrl, :listHabitats, :habitat, :virtual?]
|
53
53
|
|
54
54
|
# Public attributes which will be available on all instantiated cloud resource objects
|
55
55
|
#
|
@@ -529,7 +529,7 @@ module MU
|
|
529
529
|
images.deep_merge!(YAML.load(response))
|
530
530
|
break
|
531
531
|
end
|
532
|
-
rescue StandardError
|
532
|
+
rescue StandardError => e
|
533
533
|
if fail_hard
|
534
534
|
raise MuError, "Failed to fetch stock images from #{base_url}/#{cloud}.yaml (#{e.message})"
|
535
535
|
else
|
@@ -644,9 +644,16 @@ module MU
|
|
644
644
|
|
645
645
|
# Shorthand lookup for resource type names. Given any of the shorthand class name, configuration name (singular or plural), or full class name, return all four as a set.
|
646
646
|
# @param type [String]: A string that looks like our short or full class name or singular or plural configuration names.
|
647
|
+
# @param assert [Boolean]: Raise an exception if the type isn't valid
|
647
648
|
# @return [Array]: Class name (Symbol), singular config name (String), plural config name (String), full class name (Object)
|
648
|
-
def self.getResourceNames(type)
|
649
|
-
|
649
|
+
def self.getResourceNames(type, assert = true)
|
650
|
+
if !type
|
651
|
+
if assert
|
652
|
+
raise MuError, "nil resource type requested in getResourceNames"
|
653
|
+
else
|
654
|
+
return [nil, nil, nil, nil, {}]
|
655
|
+
end
|
656
|
+
end
|
650
657
|
@@resource_types.each_pair { |name, cloudclass|
|
651
658
|
if name == type.to_sym or
|
652
659
|
cloudclass[:cfg_name] == type or
|
@@ -656,6 +663,10 @@ module MU
|
|
656
663
|
return [type.to_sym, cloudclass[:cfg_name], cloudclass[:cfg_plural], Object.const_get("MU").const_get("Cloud").const_get(name), cloudclass]
|
657
664
|
end
|
658
665
|
}
|
666
|
+
if assert
|
667
|
+
raise MuError, "Invalid resource type #{type} requested in getResourceNames"
|
668
|
+
end
|
669
|
+
|
659
670
|
[nil, nil, nil, nil, {}]
|
660
671
|
end
|
661
672
|
|
@@ -684,6 +695,14 @@ module MU
|
|
684
695
|
@@supportedCloudList
|
685
696
|
end
|
686
697
|
|
698
|
+
# Raise an exception if the cloud provider specified isn't valid
|
699
|
+
def self.assertSupportedCloud(cloud)
|
700
|
+
if cloud.nil? or !supportedClouds.include?(cloud.to_s)
|
701
|
+
raise MuError, "Cloud provider #{cloud} is not supported"
|
702
|
+
end
|
703
|
+
Object.const_get("MU").const_get("Cloud").const_get(cloud.to_s)
|
704
|
+
end
|
705
|
+
|
687
706
|
# List of known/supported Cloud providers for which we have at least one
|
688
707
|
# set of credentials configured.
|
689
708
|
# @return [Array<String>]
|
@@ -701,6 +720,14 @@ module MU
|
|
701
720
|
available
|
702
721
|
end
|
703
722
|
|
723
|
+
# Raise an exception if the cloud provider specified isn't valid or we
|
724
|
+
# don't have any credentials configured for it.
|
725
|
+
def self.assertAvailableCloud(cloud)
|
726
|
+
if cloud.nil? or availableClouds.include?(cloud.to_s)
|
727
|
+
raise MuError, "Cloud provider #{cloud} is not available"
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
704
731
|
# Load the container class for each cloud we know about, and inject autoload
|
705
732
|
# code for each of its supported resource type classes.
|
706
733
|
failed = []
|
@@ -823,20 +850,20 @@ module MU
|
|
823
850
|
@cloud_class_cache[cloud] = {} if !@cloud_class_cache.has_key?(cloud)
|
824
851
|
begin
|
825
852
|
cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
|
826
|
-
myclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(
|
827
|
-
@@resource_types[
|
853
|
+
myclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(shortclass)
|
854
|
+
@@resource_types[shortclass.to_sym][:class].each { |class_method|
|
828
855
|
if !myclass.respond_to?(class_method) or myclass.method(class_method).owner.to_s != "#<Class:#{myclass}>"
|
829
|
-
raise MuError, "MU::Cloud::#{cloud}::#{
|
856
|
+
raise MuError, "MU::Cloud::#{cloud}::#{shortclass} has not implemented required class method #{class_method}"
|
830
857
|
end
|
831
858
|
}
|
832
|
-
@@resource_types[
|
859
|
+
@@resource_types[shortclass.to_sym][:instance].each { |instance_method|
|
833
860
|
if !myclass.public_instance_methods.include?(instance_method)
|
834
|
-
raise MuCloudResourceNotImplemented, "MU::Cloud::#{cloud}::#{
|
861
|
+
raise MuCloudResourceNotImplemented, "MU::Cloud::#{cloud}::#{shortclass} has not implemented required instance method #{instance_method}"
|
835
862
|
end
|
836
863
|
}
|
837
864
|
cloudclass.required_instance_methods.each { |instance_method|
|
838
865
|
if !myclass.public_instance_methods.include?(instance_method)
|
839
|
-
MU.log "MU::Cloud::#{cloud}::#{
|
866
|
+
MU.log "MU::Cloud::#{cloud}::#{shortclass} has not implemented required instance method #{instance_method}, will declare as attr_accessor", MU::DEBUG
|
840
867
|
end
|
841
868
|
}
|
842
869
|
|
@@ -844,7 +871,7 @@ module MU
|
|
844
871
|
return myclass
|
845
872
|
rescue NameError => e
|
846
873
|
@cloud_class_cache[cloud][type] = nil
|
847
|
-
raise MuCloudResourceNotImplemented, "The '#{type}' resource is not supported in cloud #{cloud} (tried MU::#{cloud}::#{
|
874
|
+
raise MuCloudResourceNotImplemented, "The '#{type}' resource is not supported in cloud #{cloud} (tried MU::Cloud::#{cloud}::#{shortclass})", e.backtrace
|
848
875
|
end
|
849
876
|
end
|
850
877
|
|
@@ -867,6 +894,8 @@ module MU
|
|
867
894
|
Object.const_get("MU").const_get("Cloud").const_get(name).class_eval {
|
868
895
|
attr_reader :cloudclass
|
869
896
|
attr_reader :cloudobj
|
897
|
+
attr_reader :credentials
|
898
|
+
attr_reader :config
|
870
899
|
attr_reader :destroyed
|
871
900
|
attr_reader :delayed_save
|
872
901
|
|
@@ -921,14 +950,27 @@ module MU
|
|
921
950
|
# @return [String]: Our new +deploy_id+
|
922
951
|
def intoDeploy(mommacat, force: false)
|
923
952
|
if force or (!@deploy)
|
924
|
-
MU.log "Inserting #{self}
|
953
|
+
MU.log "Inserting #{self} [#{self.object_id}] into #{mommacat.deploy_id} as a #{@config['name']}", MU::DEBUG
|
954
|
+
|
925
955
|
@deploy = mommacat
|
956
|
+
@deploy.addKitten(@cloudclass.cfg_plural, @config['name'], self)
|
926
957
|
@deploy_id = @deploy.deploy_id
|
927
958
|
@cloudobj.intoDeploy(mommacat, force: force) if @cloudobj
|
928
959
|
end
|
929
960
|
@deploy_id
|
930
961
|
end
|
931
962
|
|
963
|
+
# Return the +virtual_name+ config field, if it is set.
|
964
|
+
# @param name [String]: If set, will only return a value if +virtual_name+ matches this string
|
965
|
+
# @return [String,nil]
|
966
|
+
def virtual_name(name = nil)
|
967
|
+
if @config and @config['virtual_name'] and
|
968
|
+
(!name or name == @config['virtual_name'])
|
969
|
+
return @config['virtual_name']
|
970
|
+
end
|
971
|
+
nil
|
972
|
+
end
|
973
|
+
|
932
974
|
# @param mommacat [MU::MommaCat]: The deployment containing this cloud resource
|
933
975
|
# @param mu_name [String]: Optional- specify the full Mu resource name of an existing resource to load, instead of creating a new one
|
934
976
|
# @param cloud_id [String]: Optional- specify the cloud provider's identifier for an existing resource to load, instead of creating a new one
|
@@ -955,7 +997,6 @@ module MU
|
|
955
997
|
if my_cloud.nil? or !MU::Cloud.supportedClouds.include?(my_cloud)
|
956
998
|
raise MuError, "Can't instantiate a MU::Cloud object without a valid cloud (saw '#{my_cloud}')"
|
957
999
|
end
|
958
|
-
|
959
1000
|
@cloudclass = MU::Cloud.loadCloudType(my_cloud, self.class.shortname)
|
960
1001
|
@cloudparentclass = Object.const_get("MU").const_get("Cloud").const_get(my_cloud)
|
961
1002
|
@cloudobj = @cloudclass.new(
|
@@ -981,7 +1022,6 @@ module MU
|
|
981
1022
|
MU.log "#{self} in #{@deploy.deploy_id} didn't generate a mu_name after being loaded/initialized, dependencies on this resource will probably be confused!", MU::ERR, details: [caller, args.keys]
|
982
1023
|
end
|
983
1024
|
|
984
|
-
|
985
1025
|
# We are actually a child object invoking this via super() from its
|
986
1026
|
# own initialize(), so initialize all the attributes and instance
|
987
1027
|
# variables we know to be universal.
|
@@ -2021,17 +2061,18 @@ puts "CHOOSING #{@vpc.to_s} 'cause it has #{@config['vpc']['subnet_name']}"
|
|
2021
2061
|
loglevel = retries > 4 ? MU::NOTICE : MU::DEBUG
|
2022
2062
|
MU.log "Calling WinRM on #{@mu_name}", loglevel, details: opts
|
2023
2063
|
opts = {
|
2024
|
-
endpoint: 'https://'+@mu_name+':5986/wsman',
|
2025
2064
|
retry_limit: winrm_retries,
|
2026
2065
|
no_ssl_peer_verification: true, # XXX this should not be necessary; we get 'hostname "foo" does not match the server certificate' even when it clearly does match
|
2027
2066
|
ca_trust_path: "#{MU.mySSLDir}/Mu_CA.pem",
|
2028
2067
|
transport: :ssl,
|
2029
2068
|
operation_timeout: timeout,
|
2030
2069
|
}
|
2031
|
-
if retries % 2 == 0
|
2070
|
+
if retries % 2 == 0 # NTLM password over https
|
2071
|
+
opts[:endpoint] = 'https://'+canonical_ip+':5986/wsman'
|
2032
2072
|
opts[:user] = @config['windows_admin_username']
|
2033
2073
|
opts[:password] = getWindowsAdminPassword
|
2034
|
-
else
|
2074
|
+
else # certificate auth over https
|
2075
|
+
opts[:endpoint] = 'https://'+@mu_name+':5986/wsman'
|
2035
2076
|
opts[:client_cert] = "#{MU.mySSLDir}/#{@mu_name}-winrm.crt"
|
2036
2077
|
opts[:client_key] = "#{MU.mySSLDir}/#{@mu_name}-winrm.key"
|
2037
2078
|
end
|
data/modules/mu/clouds/aws.rb
CHANGED
@@ -33,6 +33,18 @@ module MU
|
|
33
33
|
module AdditionalResourceMethods
|
34
34
|
end
|
35
35
|
|
36
|
+
# Is this a "real" cloud provider, or a stub like CloudFormation?
|
37
|
+
def self.virtual?
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
# List all AWS projects available to our credentials
|
42
|
+
def self.listHabitats(credentials = nil)
|
43
|
+
cfg = credConfig(credentials)
|
44
|
+
return [] if !cfg or !cfg['account_number']
|
45
|
+
[cfg['account_number']]
|
46
|
+
end
|
47
|
+
|
36
48
|
# A hook that is always called just before any of the instance method of
|
37
49
|
# our resource implementations gets invoked, so that we can ensure that
|
38
50
|
# repetitive setup tasks (like resolving +:resource_group+ for Azure
|
@@ -344,6 +356,27 @@ end
|
|
344
356
|
# etc)
|
345
357
|
# @param deploy_id [MU::MommaCat]
|
346
358
|
def self.cleanDeploy(deploy_id, credentials: nil, noop: false)
|
359
|
+
|
360
|
+
if !noop
|
361
|
+
MU.log "Deleting s3://#{adminBucketName(credentials)}/#{deploy_id}-secret"
|
362
|
+
MU::Cloud::AWS.s3(credentials: credentials).delete_object(
|
363
|
+
bucket: adminBucketName(credentials),
|
364
|
+
key: "#{deploy_id}-secret"
|
365
|
+
)
|
366
|
+
listRegions(credentials: credentials).each { |r|
|
367
|
+
resp = MU::Cloud::AWS.ec2(region: r, credentials: credentials).describe_key_pairs(
|
368
|
+
filters: [{name: "key-name", values: ["deploy-#{MU.deploy_id}"]}]
|
369
|
+
)
|
370
|
+
resp.data.key_pairs.each { |keypair|
|
371
|
+
MU.log "Deleting key pair #{keypair.key_name} from #{r}"
|
372
|
+
MU::Cloud::AWS.ec2(region: r, credentials: credentials).delete_key_pair(key_name: keypair.key_name) if !noop
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
end
|
377
|
+
if hosted?
|
378
|
+
MU::Cloud::AWS.openFirewallForClients
|
379
|
+
end
|
347
380
|
end
|
348
381
|
|
349
382
|
# Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
|
@@ -1394,7 +1427,7 @@ end
|
|
1394
1427
|
# Create an AWS API client
|
1395
1428
|
# @param region [String]: Amazon region so we know what endpoint to use
|
1396
1429
|
# @param api [String]: Which API are we wrapping?
|
1397
|
-
def initialize(region:
|
1430
|
+
def initialize(region: nil, api: "EC2", credentials: nil)
|
1398
1431
|
@cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
|
1399
1432
|
@credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
|
1400
1433
|
|
@@ -1403,6 +1436,8 @@ end
|
|
1403
1436
|
end
|
1404
1437
|
|
1405
1438
|
params = {}
|
1439
|
+
region ||= MU::Cloud::AWS.credConfig(credentials)['region']
|
1440
|
+
region ||= MU.myRegion
|
1406
1441
|
|
1407
1442
|
if region
|
1408
1443
|
@region = region
|
@@ -67,16 +67,15 @@ module MU
|
|
67
67
|
# soul-crushing, yet effective
|
68
68
|
if e.message.match(/because (#{Regexp.quote(@config['region'])}[a-z]), the targeted availability zone, does not currently have sufficient capacity/)
|
69
69
|
bad_az = Regexp.last_match(1)
|
70
|
-
deletia =
|
70
|
+
deletia = []
|
71
71
|
mySubnets.each { |subnet|
|
72
|
-
if subnet.az == bad_az
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
deletia << subnet.cloud_id if subnet.az == bad_az
|
73
|
+
}
|
74
|
+
raise e if deletia.empty?
|
75
|
+
MU.log "#{bad_az} does not have EKS capacity. Dropping unsupported subnets from ContainerCluster '#{@config['name']}' and retrying.", MU::NOTICE, details: deletia
|
76
|
+
deletia.each { |subnet|
|
77
|
+
params[:resources_vpc_config][:subnet_ids].delete(subnet)
|
76
78
|
}
|
77
|
-
raise e if deletia.nil?
|
78
|
-
MU.log "#{bad_az} does not have EKS capacity. Dropping #{deletia} from ContainerCluster '#{@config['name']}' and retrying.", MU::NOTICE
|
79
|
-
params[:resources_vpc_config][:subnet_ids].delete(deletia)
|
80
79
|
end
|
81
80
|
}
|
82
81
|
|
@@ -1372,6 +1371,9 @@ MU.log c.name, MU::NOTICE, details: t
|
|
1372
1371
|
"name" => cluster["name"]+"pods",
|
1373
1372
|
"phase" => "groom"
|
1374
1373
|
}
|
1374
|
+
if !MU::Master.kubectl
|
1375
|
+
MU.log "Since I can't find a kubectl executable, you will have to handle all service account, user, and role bindings manually!", MU::WARN
|
1376
|
+
end
|
1375
1377
|
end
|
1376
1378
|
|
1377
1379
|
if MU::Cloud::AWS.isGovCloud?(cluster["region"]) and cluster["flavor"] == "EKS"
|
@@ -1470,6 +1472,11 @@ MU.log c.name, MU::NOTICE, details: t
|
|
1470
1472
|
end
|
1471
1473
|
|
1472
1474
|
if cluster["flavor"] == "EKS"
|
1475
|
+
|
1476
|
+
if !MU::Master.kubectl
|
1477
|
+
MU.log "Without a kubectl executable, I cannot bind IAM roles to EKS worker nodes", MU::ERR
|
1478
|
+
ok = false
|
1479
|
+
end
|
1473
1480
|
worker_pool["canned_iam_policies"] = [
|
1474
1481
|
"AmazonEKSWorkerNodePolicy",
|
1475
1482
|
"AmazonEKS_CNI_Policy",
|
@@ -1602,19 +1609,21 @@ MU.log c.name, MU::NOTICE, details: t
|
|
1602
1609
|
raise MuError, "Failed to apply #{authmap_cmd}" if $?.exitstatus != 0
|
1603
1610
|
end
|
1604
1611
|
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1612
|
+
if MU::Master.kubectl
|
1613
|
+
admin_user_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{MU.myRoot}/extras/admin-user.yaml"}
|
1614
|
+
admin_role_cmd = %Q{#{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f "#{MU.myRoot}/extras/admin-role-binding.yaml"}
|
1615
|
+
MU.log "Configuring Kubernetes admin-user and role", MU::NOTICE, details: admin_user_cmd+"\n"+admin_role_cmd
|
1616
|
+
%x{#{admin_user_cmd}}
|
1617
|
+
%x{#{admin_role_cmd}}
|
1618
|
+
|
1619
|
+
if @config['kubernetes_resources']
|
1620
|
+
MU::Master.applyKubernetesResources(
|
1621
|
+
@config['name'],
|
1622
|
+
@config['kubernetes_resources'],
|
1623
|
+
kubeconfig: kube_conf,
|
1624
|
+
outputdir: @deploy.deploy_dir
|
1625
|
+
)
|
1626
|
+
end
|
1618
1627
|
end
|
1619
1628
|
|
1620
1629
|
MU.log %Q{How to interact with your EKS cluster\nkubectl --kubeconfig "#{kube_conf}" get all\nkubectl --kubeconfig "#{kube_conf}" create -f some_k8s_deploy.yml\nkubectl --kubeconfig "#{kube_conf}" get nodes}, MU::SUMMARY
|
@@ -1186,7 +1186,7 @@ end
|
|
1186
1186
|
statement["Resource"] << id+"/*"
|
1187
1187
|
end
|
1188
1188
|
else
|
1189
|
-
raise MuError, "Couldn't find a #{target["
|
1189
|
+
raise MuError, "Couldn't find a #{target["type"]} named #{target["identifier"]} when generating IAM policy"
|
1190
1190
|
end
|
1191
1191
|
else
|
1192
1192
|
target["identifier"] += target["path"] if target["path"]
|
@@ -1270,7 +1270,11 @@ module MU
|
|
1270
1270
|
def peerWith(peer)
|
1271
1271
|
peer_ref = MU::Config::Ref.get(peer['vpc'])
|
1272
1272
|
peer_obj = peer_ref.kitten
|
1273
|
-
peer_id = peer_ref.cloud_id
|
1273
|
+
peer_id = peer_ref.kitten.cloud_id
|
1274
|
+
if peer_id == @cloud_id
|
1275
|
+
MU.log "#{@mu_name} attempted to peer with itself (#{@cloud_id})", MU::ERR, details: peer
|
1276
|
+
raise "#{@mu_name} attempted to peer with itself (#{@cloud_id})"
|
1277
|
+
end
|
1274
1278
|
|
1275
1279
|
if peer_obj and peer_obj.config['peers']
|
1276
1280
|
peer_obj.config['peers'].each { |peerpeer|
|
data/modules/mu/clouds/azure.rb
CHANGED
@@ -47,6 +47,11 @@ module MU
|
|
47
47
|
guid_chunks.join("-")
|
48
48
|
end
|
49
49
|
|
50
|
+
# List all Azure subscriptions available to our credentials
|
51
|
+
def self.listHabitats(credentials = nil)
|
52
|
+
[]
|
53
|
+
end
|
54
|
+
|
50
55
|
# A hook that is always called just before any of the instance method of
|
51
56
|
# our resource implementations gets invoked, so that we can ensure that
|
52
57
|
# repetitive setup tasks (like resolving +:resource_group+ for Azure
|
@@ -77,6 +82,11 @@ module MU
|
|
77
82
|
[:resource_group]
|
78
83
|
end
|
79
84
|
|
85
|
+
# Is this a "real" cloud provider, or a stub like CloudFormation?
|
86
|
+
def self.virtual?
|
87
|
+
false
|
88
|
+
end
|
89
|
+
|
80
90
|
# Stub class to represent Azure's resource identifiers, which look like:
|
81
91
|
# /subscriptions/3d20ddd8-4652-4074-adda-0d127ef1f0e0/resourceGroups/mu/providers/Microsoft.Network/virtualNetworks/mu-vnet
|
82
92
|
# Various API calls need chunks of this in different contexts, and this
|
@@ -28,6 +28,16 @@ module MU
|
|
28
28
|
|
29
29
|
@@cloudformation_mode = false
|
30
30
|
|
31
|
+
# Is this a "real" cloud provider, or a stub like CloudFormation?
|
32
|
+
def self.virtual?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
# List all AWS projects available to our credentials
|
37
|
+
def self.listHabitats(credentials = nil)
|
38
|
+
MU::Cloud::AWS.listHabitats(credentials)
|
39
|
+
end
|
40
|
+
|
31
41
|
# Return what we think of as a cloud object's habitat. In AWS, this means
|
32
42
|
# the +account_number+ in which it's resident. If this is not applicable,
|
33
43
|
# such as for a {Habitat} or {Folder}, returns nil.
|
data/modules/mu/clouds/google.rb
CHANGED
@@ -52,6 +52,11 @@ module MU
|
|
52
52
|
[:url]
|
53
53
|
end
|
54
54
|
|
55
|
+
# Is this a "real" cloud provider, or a stub like CloudFormation?
|
56
|
+
def self.virtual?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
55
60
|
# Most of our resource implementation +find+ methods have to mangle their
|
56
61
|
# args to make sure they've extracted a project or location argument from
|
57
62
|
# other available information. This does it for them.
|
@@ -337,6 +342,7 @@ module MU
|
|
337
342
|
# etc)
|
338
343
|
# @param deploy_id [MU::MommaCat]
|
339
344
|
def self.cleanDeploy(deploy_id, credentials: nil, noop: false)
|
345
|
+
removeDeploySecretsAndRoles(deploy_id, noop: noop, credentials: credentials)
|
340
346
|
end
|
341
347
|
|
342
348
|
# Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
|
@@ -548,7 +554,7 @@ MU.log e.message, MU::WARN, details: e.inspect
|
|
548
554
|
begin
|
549
555
|
listRegions(credentials: credentials)
|
550
556
|
listInstanceTypes(credentials: credentials)
|
551
|
-
|
557
|
+
listHabitats(credentials)
|
552
558
|
rescue ::Google::Apis::ClientError
|
553
559
|
MU.log "Found machine credentials #{@@svc_account_name}, but these don't appear to have sufficient permissions or scopes", MU::WARN, details: scopes
|
554
560
|
@@authorizers.delete(credentials)
|
@@ -701,12 +707,20 @@ MU.log e.message, MU::WARN, details: e.inspect
|
|
701
707
|
end
|
702
708
|
|
703
709
|
# List all Google Cloud Platform projects available to our credentials
|
704
|
-
def self.
|
710
|
+
def self.listHabitats(credentials = nil)
|
705
711
|
cfg = credConfig(credentials)
|
706
|
-
return [] if !cfg
|
712
|
+
return [] if !cfg
|
713
|
+
if cfg['restrict_to_habitats'] and cfg['restrict_to_habitats'].is_a?(Array)
|
714
|
+
cfg['restrict_to_habitats'] << cfg['project'] if cfg['project']
|
715
|
+
return cfg['restrict_to_habitats'].uniq
|
716
|
+
end
|
707
717
|
result = MU::Cloud::Google.resource_manager(credentials: credentials).list_projects
|
708
718
|
result.projects.reject! { |p| p.lifecycle_state == "DELETE_REQUESTED" }
|
709
|
-
result.projects.map { |p| p.project_id }
|
719
|
+
allprojects = result.projects.map { |p| p.project_id }
|
720
|
+
if cfg['ignore_habitats'] and cfg['ignore_habitats'].is_a?(Array)
|
721
|
+
allprojects.reject! { |p| cfg['ignore_habitats'].include?(p) }
|
722
|
+
end
|
723
|
+
allprojects
|
710
724
|
end
|
711
725
|
|
712
726
|
@@regions = {}
|
@@ -145,9 +145,9 @@ module MU
|
|
145
145
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
146
146
|
# @return [void]
|
147
147
|
def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
|
148
|
-
flags["
|
148
|
+
flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
|
149
149
|
|
150
|
-
resp = MU::Cloud::Google.storage(credentials: credentials).list_buckets(flags['
|
150
|
+
resp = MU::Cloud::Google.storage(credentials: credentials).list_buckets(flags['habitat'])
|
151
151
|
if resp and resp.items
|
152
152
|
resp.items.each { |bucket|
|
153
153
|
if bucket.labels and bucket.labels["mu-id"] == MU.deploy_id.downcase and (ignoremaster or bucket.labels['mu-master-ip'] == MU.mu_public_ip.gsub(/\./, "_"))
|