Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support passing license as file #732

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion apis/risingwave/v1alpha1/risingwave_license.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,21 @@ const RisingWaveLicenseKeySecretKey = "licenseKey"

// RisingWaveLicenseKey is the license configuration for RisingWave.
type RisingWaveLicenseKey struct {
// SecretName that contains the license. The license must be JWT formatted JSON and stored with key `licenseKey`.
// SecretName that contains the license. The license must be JWT formatted JSON.
// +optional
SecretName string `json:"secretName,omitempty"`

// SecretKey to the license in the Secret above. Defaults to `licenseKey`.
// +kubebuilder:default=licenseKey
SecretKey string `json:"secretKey,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm, I think it's not necessary to make this configurable. But anyway not a big deal.


// PassAsFile will pass the license as a file to the RisingWave process.
// The feature is only available in the RisingWave v2.1 and later. See
// https://github.com/risingwavelabs/risingwave/pull/18768 for more information.
//
// It's optional to set this field. If not set, the operator will deduce the value
// based on the RisingWave version. But when it comes to non-semver versions, it's
// recommended to set this field explicitly.
// +optional
PassAsFile *bool `json:"passAsFile,omitempty"`
}
7 changes: 6 additions & 1 deletion apis/risingwave/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -30326,9 +30326,24 @@ spec:
licenseKey:
description: LicenseKey to enable paid features of RisingWave.
properties:
passAsFile:
description: |-
PassAsFile will pass the license as a file to the RisingWave process.
The feature is only available in the RisingWave v2.1 and later. See
https://github.com/risingwavelabs/risingwave/pull/18768 for more information.

It's optional to set this field. If not set, the operator will deduce the value
based on the RisingWave version. But when it comes to non-semver versions, it's
recommended to set this field explicitly.
type: boolean
secretKey:
default: licenseKey
description: SecretKey to the license in the Secret above. Defaults
to `licenseKey`.
type: string
secretName:
description: SecretName that contains the license. The license
must be JWT formatted JSON and stored with key `licenseKey`.
must be JWT formatted JSON.
type: string
type: object
metaStore:
Expand Down
17 changes: 16 additions & 1 deletion config/risingwave-operator-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30343,9 +30343,24 @@ spec:
licenseKey:
description: LicenseKey to enable paid features of RisingWave.
properties:
passAsFile:
description: |-
PassAsFile will pass the license as a file to the RisingWave process.
The feature is only available in the RisingWave v2.1 and later. See
https://github.com/risingwavelabs/risingwave/pull/18768 for more information.

It's optional to set this field. If not set, the operator will deduce the value
based on the RisingWave version. But when it comes to non-semver versions, it's
recommended to set this field explicitly.
type: boolean
secretKey:
default: licenseKey
description: SecretKey to the license in the Secret above. Defaults
to `licenseKey`.
type: string
secretName:
description: SecretName that contains the license. The license
must be JWT formatted JSON and stored with key `licenseKey`.
must be JWT formatted JSON.
type: string
type: object
metaStore:
Expand Down
17 changes: 16 additions & 1 deletion config/risingwave-operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30343,9 +30343,24 @@ spec:
licenseKey:
description: LicenseKey to enable paid features of RisingWave.
properties:
passAsFile:
description: |-
PassAsFile will pass the license as a file to the RisingWave process.
The feature is only available in the RisingWave v2.1 and later. See
https://github.com/risingwavelabs/risingwave/pull/18768 for more information.

It's optional to set this field. If not set, the operator will deduce the value
based on the RisingWave version. But when it comes to non-semver versions, it's
recommended to set this field explicitly.
type: boolean
secretKey:
default: licenseKey
description: SecretKey to the license in the Secret above. Defaults
to `licenseKey`.
type: string
secretName:
description: SecretName that contains the license. The license
must be JWT formatted JSON and stored with key `licenseKey`.
must be JWT formatted JSON.
type: string
type: object
metaStore:
Expand Down
30 changes: 29 additions & 1 deletion docs/general/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1620,7 +1620,35 @@ string
</td>
<td>
<em>(Optional)</em>
<p>SecretName that contains the license. The license must be JWT formatted JSON and stored with key <code>licenseKey</code>.</p>
<p>SecretName that contains the license. The license must be JWT formatted JSON.</p>
</td>
</tr>
<tr>
<td>
<code>secretKey</code><br/>
<em>
string
</em>
</td>
<td>
<p>SecretKey to the license in the Secret above. Defaults to <code>licenseKey</code>.</p>
</td>
</tr>
<tr>
<td>
<code>passAsFile</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>PassAsFile will pass the license as a file to the RisingWave process.
The feature is only available in the RisingWave v2.1 and later. See
<a href="https://github.com/risingwavelabs/risingwave/pull/18768">https://github.com/risingwavelabs/risingwave/pull/18768</a> for more information.</p>
<p>It&rsquo;s optional to set this field. If not set, the operator will deduce the value
based on the RisingWave version. But when it comes to non-semver versions, it&rsquo;s
recommended to set this field explicitly.</p>
</td>
</tr>
</tbody>
Expand Down
1 change: 1 addition & 0 deletions pkg/factory/envs/risingwave.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const (
RWSslCert = "RW_SSL_CERT"
RWSslKey = "RW_SSL_KEY"
RWLicenseKey = "RW_LICENSE_KEY"
RWLicenseKeyPath = "RW_LICENSE_KEY_PATH"
)

// MinIO.
Expand Down
130 changes: 110 additions & 20 deletions pkg/factory/risingwave_object_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const (
risingwaveExecutablePath = "/risingwave/bin/risingwave"
risingwaveConfigMountPath = "/risingwave/config"
risingwaveConfigFileName = "risingwave.toml"
risingwaveLicenseKeyPath = "/license/license.jwt"
)

var (
Expand Down Expand Up @@ -502,24 +503,107 @@ func (f *RisingWaveObjectFactory) getDataDirectory() string {
return path.Join(rootPath, stateStore.DataDirectory)
}

func (f *RisingWaveObjectFactory) useLicenseFile() bool {
// If PassAsFile is set, use it.
licenseKey := f.risingwave.Spec.LicenseKey
if licenseKey != nil && licenseKey.PassAsFile != nil {
return *licenseKey.PassAsFile
}

var imageVersion = utils.GetVersionFromImage(f.risingwave.Spec.Image)

// Cases:
// v0.x is not supported.
// v1.x is not supported.
// v2.0.x uses the license key in the environment variable.
return !(strings.HasPrefix(imageVersion, "v0.") ||
strings.HasPrefix(imageVersion, "v1.") ||
strings.HasPrefix(imageVersion, "v2.0."))
}

func (f *RisingWaveObjectFactory) setupVolumeAndVolumeMountForLicenseKey(podSpec *corev1.PodSpec, container *corev1.Container) {
volumes, volumeMounts := f.volumeAndVolumeMountForLicenseKey()
container.VolumeMounts = append(container.VolumeMounts, volumeMounts...)
podSpec.Volumes = append(podSpec.Volumes, volumes...)
}

func (f *RisingWaveObjectFactory) volumeAndVolumeMountForLicenseKey() ([]corev1.Volume, []corev1.VolumeMount) {
licenseKey := f.risingwave.Spec.LicenseKey
if licenseKey == nil || licenseKey.SecretName == "" {
return nil, nil
}
if !f.useLicenseFile() {
return nil, nil
}

const volumeName = "license"

secretKey := licenseKey.SecretKey
if secretKey == "" {
secretKey = risingwavev1alpha1.RisingWaveLicenseKeySecretKey
}
volumes := []corev1.Volume{
{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: licenseKey.SecretName,
Items: []corev1.KeyToPath{
{
Key: secretKey,
Path: path.Base(risingwaveLicenseKeyPath),
},
},
},
},
},
}

volumeMounts := []corev1.VolumeMount{
{
Name: volumeName,
ReadOnly: true,
MountPath: path.Dir(risingwaveLicenseKeyPath),
},
}

return volumes, volumeMounts
}

func (f *RisingWaveObjectFactory) envsForLicenseKey() []corev1.EnvVar {
licenseKey := f.risingwave.Spec.LicenseKey
if licenseKey != nil && licenseKey.SecretName != "" {
if licenseKey == nil || licenseKey.SecretName == "" {
return nil
}

// License key in path. Applicable for versions >= 2.1.0.
if f.useLicenseFile() {
return []corev1.EnvVar{
{
Name: envs.RWLicenseKey,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: licenseKey.SecretName,
},
Key: risingwavev1alpha1.RisingWaveLicenseKeySecretKey,
Name: envs.RWLicenseKeyPath,
Value: risingwaveLicenseKeyPath,
},
}
}

// License key in environment variable. Applicable only for v2.0.x.
secretKey := licenseKey.SecretKey
if secretKey == "" {
secretKey = risingwavev1alpha1.RisingWaveLicenseKeySecretKey
}
return []corev1.EnvVar{
{
Name: envs.RWLicenseKey,
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: licenseKey.SecretName,
},
Key: secretKey,
},
},
}
},
}
return nil
}

func (f *RisingWaveObjectFactory) coreEnvsForMeta(image string) []corev1.EnvVar {
Expand Down Expand Up @@ -1394,7 +1478,7 @@ func basicSetupRisingWaveContainer(container *corev1.Container, component *risin
container.TTY = false
}

func (f *RisingWaveObjectFactory) setupMetaContainer(container *corev1.Container) {
func (f *RisingWaveObjectFactory) setupMetaContainer(podSpec *corev1.PodSpec, container *corev1.Container) {
container.Name = "meta"
container.Args = []string{"meta-node"}
container.Ports = f.portsForMetaContainer()
Expand Down Expand Up @@ -1423,6 +1507,9 @@ func (f *RisingWaveObjectFactory) setupMetaContainer(container *corev1.Container
container.VolumeMounts = mergeListWhenKeyEquals(container.VolumeMounts, f.volumeMountForConfig(), func(a, b *corev1.VolumeMount) bool {
return a.MountPath == b.MountPath
})

// Set the license key.
f.setupVolumeAndVolumeMountForLicenseKey(podSpec, container)
}

func rollingUpdateOrDefault(rollingUpdate *risingwavev1alpha1.RisingWaveNodeGroupRollingUpdate) risingwavev1alpha1.RisingWaveNodeGroupRollingUpdate {
Expand Down Expand Up @@ -1535,7 +1622,7 @@ func newPodSpecFromNodeGroupTemplate(template *risingwavev1alpha1.RisingWaveNode
return podTemplateSpec
}

func (f *RisingWaveObjectFactory) buildPodTemplateFromNodeGroup(component string, nodeGroup *risingwavev1alpha1.RisingWaveNodeGroup, setupRisingWaveContainer func(container *corev1.Container)) corev1.PodTemplateSpec {
func (f *RisingWaveObjectFactory) buildPodTemplateFromNodeGroup(component string, nodeGroup *risingwavev1alpha1.RisingWaveNodeGroup, setupRisingWaveContainer func(podSpec *corev1.PodSpec, container *corev1.Container)) corev1.PodTemplateSpec {
podTemplate := newPodSpecFromNodeGroupTemplate(&nodeGroup.Template)

// Inject system labels.
Expand All @@ -1555,7 +1642,7 @@ func (f *RisingWaveObjectFactory) buildPodTemplateFromNodeGroup(component string
})

// Run container setup for RisingWave's container.
setupRisingWaveContainer(&podTemplate.Spec.Containers[0])
setupRisingWaveContainer(&podTemplate.Spec, &podTemplate.Spec.Containers[0])

// Keep the pod spec consistent.
keepPodSpecConsistent(&podTemplate.Spec)
Expand Down Expand Up @@ -1678,7 +1765,7 @@ func (f *RisingWaveObjectFactory) convertStandaloneSpecIntoComponent() *risingwa
}

func (f *RisingWaveObjectFactory) newPodTemplateSpecFromNodeGroupByComponent(component string, nodeGroup *risingwavev1alpha1.RisingWaveNodeGroup) corev1.PodTemplateSpec {
var containerModifier func(container *corev1.Container)
var containerModifier func(podSpec *corev1.PodSpec, container *corev1.Container)
var componentPtr *risingwavev1alpha1.RisingWaveComponent
switch component {
case consts.ComponentMeta:
Expand All @@ -1700,9 +1787,9 @@ func (f *RisingWaveObjectFactory) newPodTemplateSpecFromNodeGroupByComponent(com
panic("invalid component")
}

return f.buildPodTemplateFromNodeGroup(component, nodeGroup, func(container *corev1.Container) {
return f.buildPodTemplateFromNodeGroup(component, nodeGroup, func(podSpec *corev1.PodSpec, container *corev1.Container) {
basicSetupRisingWaveContainer(container, componentPtr)
containerModifier(container)
containerModifier(podSpec, container)
})
}

Expand Down Expand Up @@ -1739,7 +1826,7 @@ func (f *RisingWaveObjectFactory) isEmbeddedServingModeEnabled() bool {
return ptr.Deref(f.risingwave.Spec.EnableEmbeddedServingMode, false)
}

func (f *RisingWaveObjectFactory) setupFrontendContainer(container *corev1.Container) {
func (f *RisingWaveObjectFactory) setupFrontendContainer(podSpec *corev1.PodSpec, container *corev1.Container) {
container.Name = "frontend"

if f.isEmbeddedServingModeEnabled() {
Expand Down Expand Up @@ -1804,7 +1891,7 @@ func (f *RisingWaveObjectFactory) portsForComputeContainer() []corev1.ContainerP
}
}

func (f *RisingWaveObjectFactory) setupComputeContainer(container *corev1.Container) {
func (f *RisingWaveObjectFactory) setupComputeContainer(podSpec *corev1.PodSpec, container *corev1.Container) {
container.Name = "compute"
container.Args = []string{"compute-node"}

Expand Down Expand Up @@ -1851,7 +1938,7 @@ func (f *RisingWaveObjectFactory) portsForCompactorContainer() []corev1.Containe
}
}

func (f *RisingWaveObjectFactory) setupCompactorContainer(container *corev1.Container) {
func (f *RisingWaveObjectFactory) setupCompactorContainer(podSpec *corev1.PodSpec, container *corev1.Container) {
container.Name = "compactor"
container.Args = []string{"compactor-node"}

Expand Down Expand Up @@ -1958,7 +2045,7 @@ func (f *RisingWaveObjectFactory) portsForStandaloneContainer() []corev1.Contain
}
}

func (f *RisingWaveObjectFactory) setupStandaloneContainer(container *corev1.Container) {
func (f *RisingWaveObjectFactory) setupStandaloneContainer(podSpec *corev1.PodSpec, container *corev1.Container) {
container.Name = "standalone"

var imageVersion = utils.GetVersionFromImage(f.risingwave.Spec.Image)
Expand Down Expand Up @@ -2024,6 +2111,9 @@ func (f *RisingWaveObjectFactory) setupStandaloneContainer(container *corev1.Con

container.VolumeMounts = mergeListWhenKeyEquals(container.VolumeMounts, f.volumeMountForConfig(),
func(a, b *corev1.VolumeMount) bool { return a.MountPath == b.MountPath })

// Set the license key.
f.setupVolumeAndVolumeMountForLicenseKey(podSpec, container)
}

func (f *RisingWaveObjectFactory) convertStandaloneIntoNodeGroup() *risingwavev1alpha1.RisingWaveNodeGroup {
Expand Down
Loading
Loading