Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Joaquim Moreno Prusi <joaquim@redhat.com>
  • Loading branch information
jmprusi committed Jul 3, 2023
1 parent 70407ba commit 26e844e
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 5 deletions.
30 changes: 28 additions & 2 deletions internal/controllers/operator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
}
// Ensure a BundleDeployment exists with its bundle source from the bundle
// image we just looked up in the solution.
dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner)
dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner, bundleEntity)
if err := r.ensureBundleDeployment(ctx, dep); err != nil {
// originally Reason: operatorsv1alpha1.ReasonInstallationFailed
op.Status.InstalledBundleResource = ""
Expand Down Expand Up @@ -260,18 +260,44 @@ func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Soluti
return nil, fmt.Errorf("entity for package %q not found in solution", packageName)
}

func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string) *unstructured.Unstructured {
func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string, bundleEntity *entities.BundleEntity) *unstructured.Unstructured {

Check failure on line 263 in internal/controllers/operator_controller.go

View workflow job for this annotation

GitHub Actions / lint

`(*OperatorReconciler).generateExpectedBundleDeployment` - `bundlePath` is unused (unparam)
// We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver.
// If you use a typed object, any default values from that struct get serialized into the JSON patch, which could
// cause unrelated fields to be patched back to the default value even though that isn't the intention. Using an
// unstructured ensures that the patch contains only what is specified. Using unstructured like this is basically
// identical to "kubectl apply -f"

// TODO(jmprusi): return err in func?
bundlePath, err := bundleEntity.BundlePath()

Check failure on line 271 in internal/controllers/operator_controller.go

View workflow job for this annotation

GitHub Actions / lint

SA4009(related information): assignment to bundlePath (staticcheck)
if err != nil {
return nil
}

channelName, err := bundleEntity.ChannelName()
if err != nil {
return nil
}

packageName, err := bundleEntity.PackageName()
if err != nil {
return nil
}

packageVersion, err := bundleEntity.Version()
if err != nil {
return nil
}

bd := &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": rukpakv1alpha1.GroupVersion.String(),
"kind": rukpakv1alpha1.BundleDeploymentKind,
"metadata": map[string]interface{}{
"name": o.GetName(),
"annotations": map[string]interface{}{
"operators.operatorframework.io/package": packageName,
"operators.operatorframework.io/channel": channelName,
"operators.operatorframework.io/version": packageVersion,
},
},
"spec": map[string]interface{}{
// TODO: Don't assume plain provisioner
Expand Down
7 changes: 7 additions & 0 deletions internal/resolution/entitysources/catalogdsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func (es *CatalogdEntitySource) Iterate(ctx context.Context, fn input.IteratorFu
return nil
}

type replacesProperty struct {
Replaces string `json:"replaces"`
}

func getEntities(ctx context.Context, client client.Client) (input.EntityList, error) {
entityList := input.EntityList{}
bundleMetadatas, packageMetdatas, err := fetchMetadata(ctx, client)
Expand Down Expand Up @@ -106,6 +110,9 @@ func getEntities(ctx context.Context, client client.Client) (input.EntityList, e
if catalogScopedEntryName == bundle.Name {
channelValue, _ := json.Marshal(property.Channel{ChannelName: ch.Name, Priority: 0})
props[property.TypeChannel] = string(channelValue)
// TODO(jmprusi): Add the proper PropertyType for this
replacesValue, _ := json.Marshal(replacesProperty{Replaces: b.Replaces})
props["olm.replaces"] = string(replacesValue)
entity := input.Entity{
ID: deppy.IdentifierFromString(fmt.Sprintf("%s%s%s", bundle.Name, bundle.Spec.Package, ch.Name)),
Properties: props,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package installedpackage

import (
"context"
"encoding/json"
"fmt"

"github.com/blang/semver/v4"
"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/constraint"
"github.com/operator-framework/deppy/pkg/deppy/input"

Check failure on line 12 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

File is not `goimports`-ed with -local github.com/operator-framework/operator-controller (goimports)
olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities"
"github.com/operator-framework/operator-controller/internal/resolution/util/predicates"
"github.com/operator-framework/operator-controller/internal/resolution/util/sort"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
)

type InstalledPackageVariable struct {

Check warning on line 19 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

exported: type name will be used as installedpackage.InstalledPackageVariable by other packages, and that stutters; consider calling this Variable (revive)
*input.SimpleVariable
bundleEntities []*olmentity.BundleEntity
}

func (r *InstalledPackageVariable) BundleEntities() []*olmentity.BundleEntity {
return r.bundleEntities
}

func NewInstalledPackageVariable(packageName string, bundleEntities []*olmentity.BundleEntity) *InstalledPackageVariable {
id := deppy.IdentifierFromString(fmt.Sprintf("installed package %s", packageName))
var entityIDs []deppy.Identifier

Check failure on line 30 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

Consider pre-allocating `entityIDs` (prealloc)
for _, bundle := range bundleEntities {
entityIDs = append(entityIDs, bundle.ID)
}
return &InstalledPackageVariable{
SimpleVariable: input.NewSimpleVariable(id, constraint.Mandatory(), constraint.Dependency(entityIDs...)),
bundleEntities: bundleEntities,
}
}

var _ input.VariableSource = &InstalledPackageVariableSource{}

type InstalledPackageVariableSource struct {

Check warning on line 42 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

exported: type name will be used as installedpackage.InstalledPackageVariableSource by other packages, and that stutters; consider calling this VariableSource (revive)
packageName string
version semver.Version
channelName string
}

func NewInstalledPackageVariableSource(packageName, version, channel string) (*InstalledPackageVariableSource, error) {
if packageName == "" {
return nil, fmt.Errorf("package name must not be empty")
}

semverVersion, err := semver.Parse(version)
if err != nil {
return nil, err
}

//TODO(jmprusi): check version and channel
return &InstalledPackageVariableSource{
packageName: packageName,
version: semverVersion,
channelName: channel,
}, nil
}

// TODO(jmprusi): move this somewhere else?
type replacesProperty struct {
Replaces string `json:"replaces"`
}

// TODO(jmprusi): move this somewhere else?
type packageProperty struct {
Package string `json:"packageName"`
Version string `json:"version"`
}

func (r *InstalledPackageVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
validRange, err := semver.ParseRange(">=" + r.version.String())
if err != nil {
return nil, err
}
resultSet, err := entitySource.Filter(ctx, input.And(predicates.WithPackageName(r.packageName), predicates.InChannel(r.channelName), predicates.InSemverRange(validRange)))
if err != nil {
return nil, err
}
if len(resultSet) == 0 {
return nil, r.notFoundError()
}
resultSet = resultSet.Sort(sort.ByChannelAndVersion)
var bundleEntities []*olmentity.BundleEntity
for i := 0; i < len(resultSet); i++ {

Check failure on line 91 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary leading newline (whitespace)

replacesJSON := resultSet[i].Properties["olm.replaces"]
packageJSON := resultSet[i].Properties["olm.package"]

if replacesJSON == "" || packageJSON == "" {
continue
}

// unmarshal replaces and packages
var replaces replacesProperty
var packages packageProperty
if err := json.Unmarshal([]byte(replacesJSON), &replaces); err != nil {
return nil, err
}
if err := json.Unmarshal([]byte(packageJSON), &packages); err != nil {
return nil, err
}

version, err := semver.Parse(packages.Version)
if err != nil {
return nil, err
}

expectedReplace := fmt.Sprintf("%s.v%s", r.packageName, r.version.String())
if r.version.Equals(version) || replaces.Replaces == expectedReplace {
bundleEntities = append(bundleEntities, olmentity.NewBundleEntity(&resultSet[i]))
}

Check failure on line 119 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)
}
return []deppy.Variable{
NewInstalledPackageVariable(r.packageName, bundleEntities),
}, nil
}

func (r *InstalledPackageVariableSource) notFoundError() error {
return fmt.Errorf("package '%s' not installed", r.packageName)
}

func NewInstalledPackage(bundleDeployment *rukpakv1alpha1.BundleDeployment) (*InstalledPackageVariableSource, error) {

Check failure on line 130 in internal/resolution/variable_sources/installedpackage/installed_package.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary leading newline (whitespace)

// TODO(jmprusi): proper if ... validation
version := bundleDeployment.Annotations["operators.operatorframework.io/version"]
channel := bundleDeployment.Annotations["operators.operatorframework.io/channel"]
pkg := bundleDeployment.Annotations["operators.operatorframework.io/package"]

return NewInstalledPackageVariableSource(pkg, version, channel)
}
25 changes: 22 additions & 3 deletions internal/resolution/variablesources/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (

"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
"sigs.k8s.io/controller-runtime/pkg/client"

operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"

Check failure on line 8 in internal/resolution/variablesources/operator.go

View workflow job for this annotation

GitHub Actions / lint

File is not `goimports`-ed with -local github.com/operator-framework/operator-controller (goimports)
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/installedpackage"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var _ input.VariableSource = &OperatorVariableSource{}
Expand Down Expand Up @@ -48,5 +49,23 @@ func (o *OperatorVariableSource) GetVariables(ctx context.Context, entitySource
variableSources = append(variableSources, rps)
}

return variableSources.GetVariables(ctx, entitySource)
bundleDeployments := rukpakv1alpha1.BundleDeploymentList{}
if err := o.client.List(ctx, &bundleDeployments); err != nil {
return nil, err
}

for _, bundleDeployment := range bundleDeployments.Items {
if _, ok := bundleDeployment.Annotations["operators.operatorframework.io/package"]; !ok {
continue
}
ips, err := installedpackage.NewInstalledPackage(&bundleDeployment)

Check failure on line 61 in internal/resolution/variablesources/operator.go

View workflow job for this annotation

GitHub Actions / lint

G601: Implicit memory aliasing in for loop. (gosec)
if err != nil {
return nil, err
}
variableSources = append(variableSources, ips)
}

// build variable source pipeline
variableSource := NewCRDUniquenessConstraintsVariableSource(NewBundlesAndDepsVariableSource(variableSources...))
return variableSource.GetVariables(ctx, entitySource)
}

0 comments on commit 26e844e

Please sign in to comment.