| 183 | } |
| 184 | |
| 185 | func (i *Install) installCRDs(crds []chart.CRD) error { |
| 186 | // We do these one file at a time in the order they were read. |
| 187 | totalItems := []*resource.Info{} |
| 188 | for _, obj := range crds { |
| 189 | if obj.File == nil { |
| 190 | return fmt.Errorf("failed to install CRD %s: file is empty", obj.Name) |
| 191 | } |
| 192 | |
| 193 | if obj.File.Data == nil { |
| 194 | return fmt.Errorf("failed to install CRD %s: file data is empty", obj.Name) |
| 195 | } |
| 196 | |
| 197 | // Read in the resources |
| 198 | res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.File.Data), false) |
| 199 | if err != nil { |
| 200 | return fmt.Errorf("failed to install CRD %s: %w", obj.Name, err) |
| 201 | } |
| 202 | |
| 203 | if len(res) == 0 { |
| 204 | return fmt.Errorf("failed to install CRD %s: resources are empty", obj.Name) |
| 205 | } |
| 206 | |
| 207 | // Send them to Kube |
| 208 | if _, err := i.cfg.KubeClient.Create( |
| 209 | res, |
| 210 | kube.ClientCreateOptionServerSideApply(i.ServerSideApply, i.ForceConflicts)); err != nil { |
| 211 | // If the error is CRD already exists, continue. |
| 212 | if apierrors.IsAlreadyExists(err) { |
| 213 | crdName := obj.Name |
| 214 | i.cfg.Logger().Debug("CRD is already present. Skipping", "crd", crdName) |
| 215 | continue |
| 216 | } |
| 217 | return fmt.Errorf("failed to install CRD %s: %w", obj.Name, err) |
| 218 | } |
| 219 | totalItems = append(totalItems, res...) |
| 220 | } |
| 221 | if len(totalItems) > 0 { |
| 222 | var waiter kube.Waiter |
| 223 | var err error |
| 224 | if c, supportsOptions := i.cfg.KubeClient.(kube.InterfaceWaitOptions); supportsOptions { |
| 225 | waiter, err = c.GetWaiterWithOptions(i.WaitStrategy, i.WaitOptions...) |
| 226 | } else { |
| 227 | waiter, err = i.cfg.KubeClient.GetWaiter(i.WaitStrategy) |
| 228 | } |
| 229 | if err != nil { |
| 230 | return fmt.Errorf("unable to get waiter: %w", err) |
| 231 | } |
| 232 | // Give time for the CRD to be recognized. |
| 233 | if err := waiter.Wait(totalItems, 60*time.Second); err != nil { |
| 234 | return err |
| 235 | } |
| 236 | |
| 237 | // If we have already gathered the capabilities, we need to invalidate |
| 238 | // the cache so that the new CRDs are recognized. This should only be |
| 239 | // the case when an action configuration is reused for multiple actions, |
| 240 | // as otherwise it is later loaded by ourselves when getCapabilities |
| 241 | // is called later on in the installation process. |
| 242 | if i.cfg.RESTClientGetter != nil { |