cloud-mu 3.1.5 → 3.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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(/\./, "_"))
|