diff --git a/apis/config/v1alpha1/license_constraints_types.go b/apis/config/v1alpha1/license_constraints_types.go new file mode 100644 index 0000000000..2e39aa2348 --- /dev/null +++ b/apis/config/v1alpha1/license_constraints_types.go @@ -0,0 +1,25 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 v1alpha1 + +type LicenseConstraints map[string]Restrictions + +type Restrictions struct { + Constraint string `json:"constraint"` + // +optional + Distributions []string `json:"distributions,omitempty"` +} diff --git a/apis/config/v1alpha1/openapi_generated.go b/apis/config/v1alpha1/openapi_generated.go index ccffd76862..aae8c2ee76 100644 --- a/apis/config/v1alpha1/openapi_generated.go +++ b/apis/config/v1alpha1/openapi_generated.go @@ -497,6 +497,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "kubedb.dev/apimachinery/apis/config/v1alpha1.GaleraArbitratorConfiguration": schema_apimachinery_apis_config_v1alpha1_GaleraArbitratorConfiguration(ref), "kubedb.dev/apimachinery/apis/config/v1alpha1.MongoDBConfiguration": schema_apimachinery_apis_config_v1alpha1_MongoDBConfiguration(ref), "kubedb.dev/apimachinery/apis/config/v1alpha1.RedisConfiguration": schema_apimachinery_apis_config_v1alpha1_RedisConfiguration(ref), + "kubedb.dev/apimachinery/apis/config/v1alpha1.Restrictions": schema_apimachinery_apis_config_v1alpha1_Restrictions(ref), "kubedb.dev/apimachinery/apis/config/v1alpha1.SinglestoreConfiguration": schema_apimachinery_apis_config_v1alpha1_SinglestoreConfiguration(ref), } } @@ -25573,6 +25574,40 @@ func schema_apimachinery_apis_config_v1alpha1_RedisConfiguration(ref common.Refe } } +func schema_apimachinery_apis_config_v1alpha1_Restrictions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "constraint": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "distributions": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"constraint"}, + }, + }, + } +} + func schema_apimachinery_apis_config_v1alpha1_SinglestoreConfiguration(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/apis/config/v1alpha1/zz_generated.deepcopy.go b/apis/config/v1alpha1/zz_generated.deepcopy.go index 880bee29a2..aab7ea2c54 100644 --- a/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -52,6 +52,28 @@ func (in *GaleraArbitratorConfiguration) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in LicenseConstraints) DeepCopyInto(out *LicenseConstraints) { + { + in := &in + *out = make(LicenseConstraints, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LicenseConstraints. +func (in LicenseConstraints) DeepCopy() LicenseConstraints { + if in == nil { + return nil + } + out := new(LicenseConstraints) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MongoDBConfiguration) DeepCopyInto(out *MongoDBConfiguration) { *out = *in @@ -116,6 +138,27 @@ func (in *RedisConfiguration) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Restrictions) DeepCopyInto(out *Restrictions) { + *out = *in + if in.Distributions != nil { + in, out := &in.Distributions, &out.Distributions + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Restrictions. +func (in *Restrictions) DeepCopy() *Restrictions { + if in == nil { + return nil + } + out := new(Restrictions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SinglestoreConfiguration) DeepCopyInto(out *SinglestoreConfiguration) { *out = *in diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index fb93270801..f8bd139954 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -19,6 +19,7 @@ package controller import ( "time" + configapi "kubedb.dev/apimachinery/apis/config/v1alpha1" cs "kubedb.dev/apimachinery/client/clientset/versioned" kubedbinformers "kubedb.dev/apimachinery/client/informers/externalversions" @@ -88,6 +89,7 @@ type Config struct { // Only watch or reconcile objects in this namespace (usually for license reasons) RestrictToNamespace string + LicenseConstraints *configapi.LicenseConstraints ResyncPeriod time.Duration ReadinessProbeInterval time.Duration MaxNumRequeues int diff --git a/pkg/license/lib.go b/pkg/license/lib.go new file mode 100644 index 0000000000..1aed4f1f63 --- /dev/null +++ b/pkg/license/lib.go @@ -0,0 +1,83 @@ +/* +Copyright AppsCode Inc. and Contributors + +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 license + +import ( + "context" + + catalogapi "kubedb.dev/apimachinery/apis/catalog/v1alpha1" + configapi "kubedb.dev/apimachinery/apis/config/v1alpha1" + + "github.com/Masterminds/semver/v3" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func MeetsLicenseConstraints(kc client.Client, constraints configapi.LicenseConstraints, dbGK schema.GroupKind, dbVersion string) (bool, error) { + if len(constraints) == 0 { + return true, nil + } + restriction, found := constraints[dbGK.Kind] + if !found { + return false, nil + } + + var dbv unstructured.Unstructured + dbv.SetGroupVersionKind(catalogapi.SchemeGroupVersion.WithKind(dbGK.Kind + "Version")) + err := kc.Get(context.TODO(), client.ObjectKey{Name: dbVersion}, &dbv) + if err != nil { + return false, err + } + + strVer, ok, err := unstructured.NestedString(dbv.UnstructuredContent(), "spec", "version") + if err != nil || !ok { + return false, err + } + v, err := semver.NewVersion(strVer) + if err != nil { + return false, err + } + + c, err := semver.NewConstraint(restriction.Constraint) + if err != nil { + return false, err + } + if !c.Check(v) { + // write reason ? + return false, nil + } + if len(restriction.Distributions) > 0 { + strDistro, ok, err := unstructured.NestedString(dbv.UnstructuredContent(), "spec", "distribution") + if err != nil || !ok { + return false, err + } + if !contains(restriction.Distributions, strDistro) { + return false, nil + } + } + return true, nil +} + +func contains(list []string, str string) bool { + for _, v := range list { + if v == str { + return true + } + } + return false +}