github.com/openshift/installer@v1.4.17/pkg/asset/quota/quota.go (about) 1 package quota 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/pkg/errors" 10 "github.com/sirupsen/logrus" 11 12 "github.com/openshift/installer/pkg/asset" 13 "github.com/openshift/installer/pkg/asset/installconfig" 14 configgcp "github.com/openshift/installer/pkg/asset/installconfig/gcp" 15 openstackvalidation "github.com/openshift/installer/pkg/asset/installconfig/openstack/validation" 16 configpowervs "github.com/openshift/installer/pkg/asset/installconfig/powervs" 17 "github.com/openshift/installer/pkg/asset/machines" 18 "github.com/openshift/installer/pkg/asset/quota/aws" 19 "github.com/openshift/installer/pkg/asset/quota/gcp" 20 "github.com/openshift/installer/pkg/asset/quota/openstack" 21 "github.com/openshift/installer/pkg/diagnostics" 22 "github.com/openshift/installer/pkg/quota" 23 quotaaws "github.com/openshift/installer/pkg/quota/aws" 24 quotagcp "github.com/openshift/installer/pkg/quota/gcp" 25 typesaws "github.com/openshift/installer/pkg/types/aws" 26 "github.com/openshift/installer/pkg/types/azure" 27 "github.com/openshift/installer/pkg/types/baremetal" 28 "github.com/openshift/installer/pkg/types/external" 29 typesgcp "github.com/openshift/installer/pkg/types/gcp" 30 "github.com/openshift/installer/pkg/types/ibmcloud" 31 "github.com/openshift/installer/pkg/types/none" 32 "github.com/openshift/installer/pkg/types/nutanix" 33 typesopenstack "github.com/openshift/installer/pkg/types/openstack" 34 "github.com/openshift/installer/pkg/types/ovirt" 35 "github.com/openshift/installer/pkg/types/powervs" 36 "github.com/openshift/installer/pkg/types/vsphere" 37 ) 38 39 // PlatformQuotaCheck is an asset that validates the install-config platform for 40 // any resource requirements based on the quotas available. 41 type PlatformQuotaCheck struct { 42 } 43 44 var _ asset.Asset = (*PlatformQuotaCheck)(nil) 45 46 // Dependencies returns the dependencies for PlatformQuotaCheck 47 func (a *PlatformQuotaCheck) Dependencies() []asset.Asset { 48 return []asset.Asset{ 49 &installconfig.InstallConfig{}, 50 &machines.Master{}, 51 &machines.Worker{}, 52 } 53 } 54 55 // Generate queries for input from the user. 56 func (a *PlatformQuotaCheck) Generate(ctx context.Context, dependencies asset.Parents) error { 57 ic := &installconfig.InstallConfig{} 58 mastersAsset := &machines.Master{} 59 workersAsset := &machines.Worker{} 60 dependencies.Get(ic, mastersAsset, workersAsset) 61 62 masters, err := mastersAsset.Machines() 63 if err != nil { 64 return err 65 } 66 67 workers, err := workersAsset.MachineSets() 68 if err != nil { 69 return err 70 } 71 72 platform := ic.Config.Platform.Name() 73 switch platform { 74 case typesaws.Name: 75 if !quotaaws.SupportedRegions.Has(ic.AWS.Region) { 76 logrus.Debugf("%s does not support API for checking quotas, therefore skipping.", ic.AWS.Region) 77 return nil 78 } 79 services := []string{"ec2", "vpc"} 80 session, err := ic.AWS.Session(ctx) 81 if err != nil { 82 return errors.Wrap(err, "failed to load AWS session") 83 } 84 q, err := quotaaws.Load(ctx, session, ic.AWS.Region, services...) 85 if quotaaws.IsUnauthorized(err) { 86 logrus.Debugf("Missing permissions to fetch Quotas and therefore will skip checking them: %v, make sure you have `servicequotas:ListAWSDefaultServiceQuotas` permission available to the user.", err) 87 logrus.Info("Skipping quota checks") 88 return nil 89 } 90 if err != nil { 91 return errors.Wrapf(err, "failed to load Quota for services: %s", strings.Join(services, ", ")) 92 } 93 instanceTypes, err := aws.InstanceTypes(ctx, session, ic.AWS.Region) 94 if quotaaws.IsUnauthorized(err) { 95 logrus.Warnf("Missing permissions to fetch instance types and therefore will skip checking Quotas: %v, make sure you have `ec2:DescribeInstanceTypes` permission available to the user.", err) 96 return nil 97 } 98 if err != nil { 99 return errors.Wrapf(err, "failed to load instance types for %s", ic.AWS.Region) 100 } 101 reports, err := quota.Check(q, aws.Constraints(ic.Config, masters, workers, instanceTypes)) 102 if err != nil { 103 return summarizeFailingReport(reports) 104 } 105 summarizeReport(reports) 106 case typesgcp.Name: 107 services := []string{"compute.googleapis.com", "iam.googleapis.com"} 108 q, err := quotagcp.Load(ctx, ic.Config.Platform.GCP.ProjectID, services...) 109 if quotagcp.IsUnauthorized(err) { 110 logrus.Warnf("Missing permissions to fetch Quotas and therefore will skip checking them: %v, make sure you have `roles/servicemanagement.quotaViewer` assigned to the user.", err) 111 return nil 112 } 113 if err != nil { 114 return errors.Wrapf(err, "failed to load Quota for services: %s", strings.Join(services, ", ")) 115 } 116 session, err := configgcp.GetSession(ctx) 117 if err != nil { 118 return errors.Wrap(err, "failed to load GCP session") 119 } 120 client, err := gcp.NewClient(ctx, session, ic.Config.Platform.GCP.ProjectID) 121 if err != nil { 122 return errors.Wrap(err, "failed to create client for quota constraints") 123 } 124 reports, err := quota.Check(q, gcp.Constraints(client, ic.Config, masters, workers)) 125 if err != nil { 126 return summarizeFailingReport(reports) 127 } 128 summarizeReport(reports) 129 case typesopenstack.Name: 130 if skip := os.Getenv("OPENSHIFT_INSTALL_SKIP_PREFLIGHT_VALIDATIONS"); skip == "1" { 131 logrus.Warnf("OVERRIDE: pre-flight validation disabled.") 132 return nil 133 } 134 ci, err := openstackvalidation.GetCloudInfo(ctx, ic.Config) 135 if err != nil { 136 return errors.Wrap(err, "failed to get cloud info") 137 } 138 if ci == nil { 139 logrus.Warnf("Empty OpenStack cloud info and therefore will skip checking quota validation.") 140 return nil 141 } 142 reports, err := quota.Check(ci.Quotas, openstack.Constraints(ci, masters, workers)) 143 if err != nil { 144 return summarizeFailingReport(reports) 145 } 146 summarizeReport(reports) 147 case powervs.Name: 148 // We need to prompt for missing variables because NewPISession requires them! 149 bxCli, err := configpowervs.NewBxClient(true) 150 if err != nil { 151 return errors.Wrap(err, "failed to create bluemix client") 152 } 153 154 err = bxCli.NewPISession() 155 if err != nil { 156 return errors.Wrap(err, "failed to create a new PISession") 157 } 158 case azure.Name, baremetal.Name, ibmcloud.Name, external.Name, none.Name, ovirt.Name, vsphere.Name, nutanix.Name: 159 // no special provisioning requirements to check 160 default: 161 err = fmt.Errorf("unknown platform type %q", platform) 162 } 163 return err 164 } 165 166 // Name returns the human-friendly name of the asset. 167 func (a *PlatformQuotaCheck) Name() string { 168 return "Platform Quota Check" 169 } 170 171 // summarizeFailingReport summarizes a report when there are failing constraints. 172 func summarizeFailingReport(reports []quota.ConstraintReport) error { 173 var notavailable []string 174 var unknown []string 175 var regionMessage string 176 for _, report := range reports { 177 switch report.Result { 178 case quota.NotAvailable: 179 if report.For.Region != "" { 180 regionMessage = " in " + report.For.Region 181 } else { 182 regionMessage = "" 183 } 184 notavailable = append(notavailable, fmt.Sprintf("%s is not available%s because %s", report.For.Name, regionMessage, report.Message)) 185 case quota.Unknown: 186 unknown = append(unknown, report.For.Name) 187 default: 188 continue 189 } 190 } 191 192 if len(notavailable) == 0 && len(unknown) > 0 { 193 // all quotas are missing information so warn and skip 194 logrus.Warnf("Failed to find information on quotas %s", strings.Join(unknown, ", ")) 195 return nil 196 } 197 198 msg := strings.Join(notavailable, ", ") 199 if len(unknown) > 0 { 200 msg = fmt.Sprintf("%s, and could not find information on %s", msg, strings.Join(unknown, ", ")) 201 } 202 return &diagnostics.Err{Reason: "MissingQuota", Message: msg} 203 } 204 205 // summarizeReport summarizes a report when there are availble. 206 func summarizeReport(reports []quota.ConstraintReport) { 207 var low []string 208 var regionMessage string 209 for _, report := range reports { 210 switch report.Result { 211 case quota.AvailableButLow: 212 if report.For.Region != "" { 213 regionMessage = " (" + report.For.Region + ")" 214 } else { 215 regionMessage = "" 216 } 217 low = append(low, fmt.Sprintf("%s%s", report.For.Name, regionMessage)) 218 default: 219 continue 220 } 221 } 222 if len(low) > 0 { 223 logrus.Warnf("Following quotas %s are available but will be completely used pretty soon.", strings.Join(low, ", ")) 224 } 225 }