// installComponents attempts to create pods to run installable images identified by the user. If an image // is installable, we check whether it requires access to the user token. If so, the caller must have // explicitly granted that access (because the token may be the user's). func (c *AppConfig) installComponents(components app.ComponentReferences) ([]runtime.Object, string, error) { if c.SkipGeneration { return nil, "", nil } jobs := components.InstallableComponentRefs() switch { case len(jobs) > 1: return nil, "", fmt.Errorf("only one installable component may be provided: %s", jobs.HumanString(", ")) case len(jobs) == 0: return nil, "", nil } job := jobs[0] if len(components) > 1 { return nil, "", fmt.Errorf("%q is installable and may not be specified with other components", job.Input().Value) } input := job.Input() imageRef, err := app.InputImageFromMatch(input.ResolvedMatch) if err != nil { return nil, "", fmt.Errorf("can't include %q: %v", input, err) } glog.V(4).Infof("Resolved match for installer %#v", input.ResolvedMatch) imageRef.AsImageStream = false imageRef.AsResolvedImage = true name := c.Name if len(name) == 0 { var ok bool name, ok = imageRef.SuggestName() if !ok { return nil, "", fmt.Errorf("can't suggest a valid name, please specify a name with --name") } } imageRef.ObjectName = name glog.V(4).Infof("Proposed installable image %#v", imageRef) generatorInput := input.ResolvedMatch.GeneratorInput if generatorInput.Token != nil && !c.AllowSecretUse || c.SecretAccessor == nil { return nil, "", ErrRequiresExplicitAccess{*input.ResolvedMatch} } pod, secret, err := imageRef.InstallablePod(generatorInput, c.SecretAccessor) if err != nil { return nil, "", err } objects := []runtime.Object{pod} if secret != nil { objects = append(objects, secret) } for i := range objects { outil.AddObjectAnnotations(objects[i], map[string]string{ GeneratedForJob: "true", GeneratedForJobFor: input.String(), }) } return objects, name, nil }
func setAnnotations(annotations map[string]string, result *newcmd.AppResult) error { for _, object := range result.List.Items { err := util.AddObjectAnnotations(object, annotations) if err != nil { return err } } return nil }
// installComponents attempts to create pods to run installable images identified by the user. If an image // is installable, we check whether it requires access to the user token. If so, the caller must have // explicitly granted that access (because the token may be the user's). func (c *AppConfig) installComponents(components app.ComponentReferences, env app.Environment) ([]runtime.Object, string, error) { if c.SkipGeneration { return nil, "", nil } jobs := components.InstallableComponentRefs() switch { case len(jobs) > 1: return nil, "", fmt.Errorf("only one installable component may be provided: %s", jobs.HumanString(", ")) case len(jobs) == 0: return nil, "", nil } job := jobs[0] if len(components) > 1 { return nil, "", fmt.Errorf("%q is installable and may not be specified with other components", job.Input().Value) } input := job.Input() imageRef, err := app.InputImageFromMatch(input.ResolvedMatch) if err != nil { return nil, "", fmt.Errorf("can't include %q: %v", input, err) } glog.V(4).Infof("Resolved match for installer %#v", input.ResolvedMatch) imageRef.AsImageStream = false imageRef.AsResolvedImage = true imageRef.Env = env name := c.Name if len(name) == 0 { var ok bool name, ok = imageRef.SuggestName() if !ok { return nil, "", fmt.Errorf("can't suggest a valid name, please specify a name with --name") } } imageRef.ObjectName = name glog.V(4).Infof("Proposed installable image %#v", imageRef) secretAccessor := c.SecretAccessor generatorInput := input.ResolvedMatch.GeneratorInput token := generatorInput.Token if token != nil && !c.AllowSecretUse || secretAccessor == nil { if !c.DryRun { return nil, "", ErrRequiresExplicitAccess{Match: *input.ResolvedMatch, Input: generatorInput} } secretAccessor = &fakeSecretAccessor{token: "FAKE_TOKEN"} } objects := []runtime.Object{} serviceAccountName := "installer" if token != nil && token.ServiceAccount { if _, err := c.KubeClient.ServiceAccounts(c.originNamespace).Get(serviceAccountName); err != nil { if kerrors.IsNotFound(err) { objects = append(objects, // create a new service account &kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: serviceAccountName}}, // grant the service account the edit role on the project (TODO: installer) &authapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{Name: "installer-role-binding"}, Subjects: []kapi.ObjectReference{{Kind: "ServiceAccount", Name: serviceAccountName}}, RoleRef: kapi.ObjectReference{Name: "edit"}, }, ) } } } pod, secret, err := imageRef.InstallablePod(generatorInput, secretAccessor, serviceAccountName) if err != nil { return nil, "", err } objects = append(objects, pod) if secret != nil { objects = append(objects, secret) } for i := range objects { outil.AddObjectAnnotations(objects[i], map[string]string{ GeneratedForJob: "true", GeneratedForJobFor: input.String(), }) } describeGeneratedJob(c.Out, job, pod, secret, c.originNamespace) return objects, name, nil }
// Stop deletes the build configuration and all of the associated builds. func (reaper *BuildConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error { noBcFound := false noBuildFound := true // Add deletion pending annotation to the build config err := unversioned.RetryOnConflict(unversioned.DefaultRetry, func() error { bc, err := reaper.oc.BuildConfigs(namespace).Get(name) if kerrors.IsNotFound(err) { noBcFound = true return nil } if err != nil { return err } // Ignore if the annotation already exists if strings.ToLower(bc.Annotations[buildapi.BuildConfigPausedAnnotation]) == "true" { return nil } // Set the annotation and update if err := util.AddObjectAnnotations(bc, map[string]string{buildapi.BuildConfigPausedAnnotation: "true"}); err != nil { return err } _, err = reaper.oc.BuildConfigs(namespace).Update(bc) return err }) if err != nil { return err } // Warn the user if the BuildConfig won't get deleted after this point. bcDeleted := false defer func() { if !bcDeleted { glog.Warningf("BuildConfig %s/%s will not be deleted because not all associated builds could be deleted. You can try re-running the command or removing them manually", namespace, name) } }() // Collect builds related to the config. builds, err := reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelector(name)}) if err != nil { return err } errList := []error{} for _, build := range builds.Items { noBuildFound = false if err := reaper.oc.Builds(namespace).Delete(build.Name); err != nil { glog.Warningf("Cannot delete Build %s/%s: %v", build.Namespace, build.Name, err) if !kerrors.IsNotFound(err) { errList = append(errList, err) } } } // Collect deprecated builds related to the config. // TODO: Delete this block after BuildConfigLabelDeprecated is removed. builds, err = reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelectorDeprecated(name)}) if err != nil { return err } for _, build := range builds.Items { noBuildFound = false if err := reaper.oc.Builds(namespace).Delete(build.Name); err != nil { glog.Warningf("Cannot delete Build %s/%s: %v", build.Namespace, build.Name, err) if !kerrors.IsNotFound(err) { errList = append(errList, err) } } } // Aggregate all errors if len(errList) > 0 { return kutilerrors.NewAggregate(errList) } // Finally we can delete the BuildConfig if !noBcFound { if err := reaper.oc.BuildConfigs(namespace).Delete(name); err != nil { return err } } bcDeleted = true if noBcFound && noBuildFound { return kerrors.NewNotFound("BuildConfig", name) } return nil }
// Stop deletes the build configuration and all of the associated builds. func (reaper *BuildConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error { _, err := reaper.oc.BuildConfigs(namespace).Get(name) if err != nil { return err } var bcPotentialBuilds []buildapi.Build // Collect builds related to the config. builds, err := reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelector(name)}) if err != nil { return err } bcPotentialBuilds = append(bcPotentialBuilds, builds.Items...) // Collect deprecated builds related to the config. // TODO: Delete this block after BuildConfigLabelDeprecated is removed. builds, err = reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelectorDeprecated(name)}) if err != nil { return err } bcPotentialBuilds = append(bcPotentialBuilds, builds.Items...) // A map of builds associated with this build configuration bcBuilds := make(map[ktypes.UID]buildapi.Build) // Because of name length limits in the BuildConfigSelector, annotations are used to ensure // reliable selection of associated builds. for _, build := range bcPotentialBuilds { if build.Annotations != nil { if bcName, ok := build.Annotations[buildapi.BuildConfigAnnotation]; ok { // The annotation, if present, has the full build config name. if bcName != name { // If the name does not match exactly, the build is not truly associated with the build configuration continue } } } // Note that if there is no annotation, this is a deprecated build spec // and we choose to include it in the deletion having matched only the BuildConfigSelectorDeprecated // Use a map to union the lists returned by the contemporary & deprecated build queries // (there will be overlap between the lists, and we only want to try to delete each build once) bcBuilds[build.UID] = build } // If there are builds associated with this build configuration, pause it before attempting the deletion if len(bcBuilds) > 0 { // Add paused annotation to the build config pending the deletion err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { bc, err := reaper.oc.BuildConfigs(namespace).Get(name) if err != nil { return err } // Ignore if the annotation already exists if strings.ToLower(bc.Annotations[buildapi.BuildConfigPausedAnnotation]) == "true" { return nil } // Set the annotation and update if err := util.AddObjectAnnotations(bc, map[string]string{buildapi.BuildConfigPausedAnnotation: "true"}); err != nil { return err } _, err = reaper.oc.BuildConfigs(namespace).Update(bc) return err }) if err != nil { return err } } // Warn the user if the BuildConfig won't get deleted after this point. bcDeleted := false defer func() { if !bcDeleted { glog.Warningf("BuildConfig %s/%s will not be deleted because not all associated builds could be deleted. You can try re-running the command or removing them manually", namespace, name) } }() // For the benefit of test cases, sort the UIDs so that the deletion order is deterministic buildUIDs := make([]string, 0, len(bcBuilds)) for buildUID := range bcBuilds { buildUIDs = append(buildUIDs, string(buildUID)) } sort.Strings(buildUIDs) errList := []error{} for _, buildUID := range buildUIDs { build := bcBuilds[ktypes.UID(buildUID)] if err := reaper.oc.Builds(namespace).Delete(build.Name); err != nil { glog.Warningf("Cannot delete Build %s/%s: %v", build.Namespace, build.Name, err) if !kerrors.IsNotFound(err) { errList = append(errList, err) } } } // Aggregate all errors if len(errList) > 0 { return kutilerrors.NewAggregate(errList) } if err := reaper.oc.BuildConfigs(namespace).Delete(name); err != nil { return err } bcDeleted = true return nil }