Skip to content

Commit

Permalink
Resolution POC
Browse files Browse the repository at this point in the history
Signed-off-by: Mikalai Radchuk <mradchuk@redhat.com>
  • Loading branch information
m1kola committed Jun 2, 2023
1 parent 754fc5a commit 7382e7d
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 28 deletions.
143 changes: 143 additions & 0 deletions cmd/resolutioncli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"context"
"errors"
"flag"
"fmt"
"os"

"github.com/operator-framework/deppy/pkg/deppy/solver"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"

catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1"
operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
"github.com/operator-framework/operator-controller/internal/resolution/entitysources"
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/bundles_and_dependencies"
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/olm"
)

var (
scheme = runtime.NewScheme()
)

func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme))
utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme))
utilruntime.Must(catalogd.AddToScheme(scheme))
}

func main() {
var packageName string
var packageVersion string
var packageChannel string
flag.StringVar(&packageName, "package-name", "", "Name of the package to resolve")
flag.StringVar(&packageVersion, "package-version", "", "Version of the package")
flag.StringVar(&packageChannel, "package-channel", "", "Channel of the package")
flag.Parse()

if err := validateFlags(packageName); err != nil {
fmt.Println(err)
flag.Usage()
os.Exit(1)
}

err := run(packageName, packageVersion, packageChannel)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func validateFlags(packageName string) error {
if packageName == "" {
return errors.New("missing required -package-name flag")
}

return nil
}

func run(packageName, packageVersion, packageChannel string) error {
ctx := context.Background()
client, err := client.New(config.GetConfigOrDie(), client.Options{Scheme: scheme})
if err != nil {
return fmt.Errorf("failed to create client: %w", err)
}

packageVariableSource := NewPackageVariableSource(packageName, packageVersion, packageChannel)
resolver, err := solver.NewDeppySolver(
entitysources.NewCatalogdEntitySource(client),
append(olm.NestedVariableSource{packageVariableSource}, olm.NewOLMVariableSource(client)...),
)
if err != nil {
return fmt.Errorf("failed to create a solver: %w", err)
}

bundleImage, err := resolve(ctx, resolver, packageName)
if err != nil {
return err
}

fmt.Println(bundleImage)
return nil
}

func resolve(ctx context.Context, resolver *solver.DeppySolver, packageName string) (string, error) {
solution, err := resolver.Solve(ctx)
if err != nil {
return "", err
}

bundleEntity, err := getBundleEntityFromSolution(solution, packageName)
if err != nil {
return "", err
}

// Get the bundle image reference for the bundle
bundleImage, err := bundleEntity.BundlePath()
if err != nil {
return "", err
}

return bundleImage, nil
}

func getBundleEntityFromSolution(solution *solver.Solution, packageName string) (*entity.BundleEntity, error) {
for _, variable := range solution.SelectedVariables() {
switch v := variable.(type) {
case *bundles_and_dependencies.BundleVariable:
entityPkgName, err := v.BundleEntity().PackageName()
if err != nil {
return nil, err
}
if packageName == entityPkgName {
return v.BundleEntity(), nil
}
}
}
return nil, fmt.Errorf("entity for package %q not found in solution", packageName)
}
27 changes: 27 additions & 0 deletions cmd/resolutioncli/variable_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"github.com/operator-framework/deppy/pkg/deppy/input"
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/olm"
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/required_package"
)

func NewPackageVariableSource(packageName, packageVersion, packageChannel string) func(inputVariableSource input.VariableSource) (input.VariableSource, error) {
return func(inputVariableSource input.VariableSource) (input.VariableSource, error) {
pkgSource, err := required_package.NewRequiredPackage(
packageName,
required_package.InVersionRange(packageVersion),
required_package.InChannel(packageChannel),
)
if err != nil {
return nil, err
}

sliceSource := olm.SliceVariableSource{pkgSource}
if inputVariableSource != nil {
sliceSource = append(sliceSource, inputVariableSource)
}

return sliceSource, nil
}
}
46 changes: 46 additions & 0 deletions internal/resolution/variable_sources/olm/composite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package olm

import (
"context"
"errors"

"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
)

var _ input.VariableSource = &SliceVariableSource{}
var _ input.VariableSource = &NestedVariableSource{}

type NestedVariableSource []func(inputVariableSource input.VariableSource) (input.VariableSource, error)

func (s NestedVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
if len(s) == 0 {
return nil, errors.New("empty nested variable sources")
}

var variableSource input.VariableSource
var err error
for _, constructor := range s {
variableSource, err = constructor(variableSource)
if err != nil {
return nil, err
}
}

return variableSource.GetVariables(ctx, entitySource)
}

type SliceVariableSource []input.VariableSource

func (s SliceVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
var variables []deppy.Variable
for _, variableSource := range s {
inputVariables, err := variableSource.GetVariables(ctx, entitySource)
if err != nil {
return nil, err
}
variables = append(variables, inputVariables...)
}

return variables, nil
}
52 changes: 24 additions & 28 deletions internal/resolution/variable_sources/olm/olm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package olm
import (
"context"

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

Expand All @@ -13,47 +12,44 @@ import (
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/required_package"
)

var _ input.VariableSource = &OLMVariableSource{}

type OLMVariableSource struct {
client client.Client
func NewOLMVariableSource(cl client.Client) NestedVariableSource {
return NestedVariableSource{
func(inputVariableSource input.VariableSource) (input.VariableSource, error) {
return NewOperatorVariableSource(cl, inputVariableSource)
},
func(inputVariableSource input.VariableSource) (input.VariableSource, error) {
return bundles_and_dependencies.NewBundlesAndDepsVariableSource(inputVariableSource), nil
},
func(inputVariableSource input.VariableSource) (input.VariableSource, error) {
return crd_constraints.NewCRDUniquenessConstraintsVariableSource(inputVariableSource), nil
},
}
}

func NewOLMVariableSource(cl client.Client) *OLMVariableSource {
return &OLMVariableSource{
client: cl,
func NewOperatorVariableSource(cl client.Client, inputVariableSource input.VariableSource) (SliceVariableSource, error) {
inputVariableSources := SliceVariableSource{}
if inputVariableSource != nil {
inputVariableSources = append(inputVariableSources, inputVariableSource)
}
}

func (o *OLMVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
operatorList := operatorsv1alpha1.OperatorList{}
if err := o.client.List(ctx, &operatorList); err != nil {
// TODO: Maybe?
if err := cl.List(context.TODO(), &operatorList); err != nil {
return nil, err
}

var inputVariableSources []input.VariableSource

// build required package variable sources
for _, operator := range operatorList.Items {
rps, err := o.requiredPackageFromOperator(&operator)
rps, err := required_package.NewRequiredPackage(
operator.Spec.PackageName,
required_package.InVersionRange(operator.Spec.Version),
required_package.InChannel(operator.Spec.Channel),
)
if err != nil {
return nil, err
}
inputVariableSources = append(inputVariableSources, rps)
}

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

func (o *OLMVariableSource) requiredPackageFromOperator(operator *operatorsv1alpha1.Operator) (*required_package.RequiredPackageVariableSource, error) {
var opts []required_package.RequiredPackageOption
if operator.Spec.Version != "" {
opts = append(opts, required_package.InVersionRange(operator.Spec.Version))
}
if operator.Spec.Channel != "" {
opts = append(opts, required_package.InChannel(operator.Spec.Channel))
}
return required_package.NewRequiredPackage(operator.Spec.PackageName, opts...)
return inputVariableSources, nil
}

0 comments on commit 7382e7d

Please sign in to comment.