Skip to content

Commit

Permalink
setting: Add 'enable-bundle-quay-fallback' option for config
Browse files Browse the repository at this point in the history
Since we are going to put bundles on quay and mirror where quay serve as
fallback solution. This PR add a config flag
`enable-bundle-quay-fallback` which allow user to download bundle from
quay.

```
$ crc config set enable-bundle-quay-fallback true

$ crc setup
INFO Getting bundle for the CRC executable
INFO Downloading bundle: /home/prkumar/.crc/cache/crc_microshift_libvirt_4.13.9_amd64.crcbundle...
INFO Unable to download bundle from mirror, falling back to quay
Getting image source signatures
Copying blob d37da0367460 done
Copying config f3021495d6 done
Writing manifest to image destination
Storing signatures
INFO Extracting the image bundle layer...
crc_microshift_libvirt_4.13.9_amd64.crcbundle:  1.46 GiB / 1.46 GiB [--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------] 100.00%
INFO Verifying the bundle signature...
INFO Uncompressing /home/prkumar/.crc/cache/crc_microshift_libvirt_4.13.9_amd64.crcbundle
crc.qcow2:  4.28 GiB / 4.28 GiB [--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------] 100.00%
oc:  141.85 MiB / 141.85 MiB [-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------] 100.00%
Your system is correctly setup for using CRC. Use 'crc start' to start the instance
```
  • Loading branch information
praveenkumar committed Sep 4, 2023
1 parent bf09ddd commit 504ffef
Show file tree
Hide file tree
Showing 14 changed files with 89 additions and 71 deletions.
2 changes: 2 additions & 0 deletions cmd/crc/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ func runStart(ctx context.Context) (*types.StartResult, error) {
EnableSharedDirs: config.Get(crcConfig.EnableSharedDirs).AsBool(),

EmergencyLogin: config.Get(crcConfig.EmergencyLogin).AsBool(),

EnableBundleQuayFallback: config.Get(crcConfig.EnableBundleQuayFallback).AsBool(),
}

client := newMachine()
Expand Down
25 changes: 13 additions & 12 deletions pkg/crc/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,19 @@ func (h *Handler) Start(c *context) error {

func getStartConfig(cfg crcConfig.Storage, args client.StartConfig) types.StartConfig {
return types.StartConfig{
BundlePath: cfg.Get(crcConfig.Bundle).AsString(),
Memory: cfg.Get(crcConfig.Memory).AsInt(),
DiskSize: cfg.Get(crcConfig.DiskSize).AsInt(),
CPUs: cfg.Get(crcConfig.CPUs).AsInt(),
NameServer: cfg.Get(crcConfig.NameServer).AsString(),
PullSecret: cluster.NewNonInteractivePullSecretLoader(cfg, args.PullSecretFile),
KubeAdminPassword: cfg.Get(crcConfig.KubeAdminPassword).AsString(),
IngressHTTPPort: cfg.Get(crcConfig.IngressHTTPPort).AsUInt(),
IngressHTTPSPort: cfg.Get(crcConfig.IngressHTTPSPort).AsUInt(),
Preset: crcConfig.GetPreset(cfg),
EnableSharedDirs: cfg.Get(crcConfig.EnableSharedDirs).AsBool(),
EmergencyLogin: cfg.Get(crcConfig.EmergencyLogin).AsBool(),
BundlePath: cfg.Get(crcConfig.Bundle).AsString(),
Memory: cfg.Get(crcConfig.Memory).AsInt(),
DiskSize: cfg.Get(crcConfig.DiskSize).AsInt(),
CPUs: cfg.Get(crcConfig.CPUs).AsInt(),
NameServer: cfg.Get(crcConfig.NameServer).AsString(),
PullSecret: cluster.NewNonInteractivePullSecretLoader(cfg, args.PullSecretFile),
KubeAdminPassword: cfg.Get(crcConfig.KubeAdminPassword).AsString(),
IngressHTTPPort: cfg.Get(crcConfig.IngressHTTPPort).AsUInt(),
IngressHTTPSPort: cfg.Get(crcConfig.IngressHTTPSPort).AsUInt(),
Preset: crcConfig.GetPreset(cfg),
EnableSharedDirs: cfg.Get(crcConfig.EnableSharedDirs).AsBool(),
EmergencyLogin: cfg.Get(crcConfig.EmergencyLogin).AsBool(),
EnableBundleQuayFallback: cfg.Get(crcConfig.EnableBundleQuayFallback).AsBool(),
}
}

Expand Down
50 changes: 27 additions & 23 deletions pkg/crc/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,30 @@ import (
)

const (
Bundle = "bundle"
CPUs = "cpus"
Memory = "memory"
DiskSize = "disk-size"
NameServer = "nameserver"
PullSecretFile = "pull-secret-file"
DisableUpdateCheck = "disable-update-check"
ExperimentalFeatures = "enable-experimental-features"
NetworkMode = "network-mode"
HostNetworkAccess = "host-network-access"
HTTPProxy = "http-proxy"
HTTPSProxy = "https-proxy"
NoProxy = "no-proxy"
ProxyCAFile = "proxy-ca-file"
ConsentTelemetry = "consent-telemetry"
EnableClusterMonitoring = "enable-cluster-monitoring"
KubeAdminPassword = "kubeadmin-password"
Preset = "preset"
EnableSharedDirs = "enable-shared-dirs"
SharedDirPassword = "shared-dir-password" // #nosec G101
IngressHTTPPort = "ingress-http-port"
IngressHTTPSPort = "ingress-https-port"
EmergencyLogin = "enable-emergency-login"
Bundle = "bundle"
CPUs = "cpus"
Memory = "memory"
DiskSize = "disk-size"
NameServer = "nameserver"
PullSecretFile = "pull-secret-file"
DisableUpdateCheck = "disable-update-check"
ExperimentalFeatures = "enable-experimental-features"
NetworkMode = "network-mode"
HostNetworkAccess = "host-network-access"
HTTPProxy = "http-proxy"
HTTPSProxy = "https-proxy"
NoProxy = "no-proxy"
ProxyCAFile = "proxy-ca-file"
ConsentTelemetry = "consent-telemetry"
EnableClusterMonitoring = "enable-cluster-monitoring"
KubeAdminPassword = "kubeadmin-password"
Preset = "preset"
EnableSharedDirs = "enable-shared-dirs"
SharedDirPassword = "shared-dir-password" // #nosec G101
IngressHTTPPort = "ingress-http-port"
IngressHTTPSPort = "ingress-https-port"
EmergencyLogin = "enable-emergency-login"
EnableBundleQuayFallback = "enable-bundle-quay-fallback"
)

func RegisterSettings(cfg *Config) {
Expand Down Expand Up @@ -135,6 +136,9 @@ func RegisterSettings(cfg *Config) {
cfg.AddSetting(IngressHTTPSPort, constants.OpenShiftIngressHTTPSPort, validatePort, RequiresHTTPSPortChangeWarning,
fmt.Sprintf("HTTPS port to use for OpenShift ingress/routes on the host (1024-65535, default: %d)", constants.OpenShiftIngressHTTPSPort))

cfg.AddSetting(EnableBundleQuayFallback, false, ValidateBool, SuccessfullyApplied,
"Enable pull bundle from quay as fallback (true/false, default: false)")

if err := cfg.RegisterNotifier(Preset, presetChanged); err != nil {
logging.Debugf("Failed to register notifier for Preset: %v", err)
}
Expand Down
11 changes: 9 additions & 2 deletions pkg/crc/machine/bundle/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"strings"
"time"

"github.com/cavaliergopher/grab/v3"

"github.com/Masterminds/semver/v3"
"github.com/crc-org/crc/pkg/crc/constants"
"github.com/crc-org/crc/pkg/crc/gpg"
Expand Down Expand Up @@ -345,15 +347,20 @@ func DownloadDefault(preset crcPreset.Preset) (string, error) {
return downloadInfo.Download(constants.GetDefaultBundlePath(preset), 0664)
}

func Download(preset crcPreset.Preset, bundleURI string) (string, error) {
func Download(preset crcPreset.Preset, bundleURI string, enableBundleQuayFallback bool) (string, error) {
// If we are asked to download
// ~/.crc/cache/crc_podman_libvirt_4.1.1.crcbundle, this means we want
// are downloading the default bundle for this release. This uses a
// different codepath from user-specified URIs as for the default
// bundles, their sha256sums are known and can be checked.
if bundleURI == constants.GetDefaultBundlePath(preset) {
if preset == crcPreset.OpenShift || preset == crcPreset.Microshift {
return DownloadDefault(preset)
downloadedBundlePath, err := DownloadDefault(preset)
if grab.IsStatusCodeError(errors.Unwrap(err)) && enableBundleQuayFallback {
logging.Info("Unable to download bundle from mirror, falling back to quay")
return image.PullBundle(preset, "")
}
return downloadedBundlePath, err
}
return image.PullBundle(preset, "")
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/crc/machine/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ import (

const minimumMemoryForMonitoring = 14336

func getCrcBundleInfo(preset crcPreset.Preset, bundleName, bundlePath string) (*bundle.CrcBundleInfo, error) {
func getCrcBundleInfo(preset crcPreset.Preset, bundleName, bundlePath string, enableBundleQuayFallback bool) (*bundle.CrcBundleInfo, error) {
bundleInfo, err := bundle.Use(bundleName)
if err == nil {
logging.Infof("Loading bundle: %s...", bundleName)
return bundleInfo, nil
}
logging.Debugf("Failed to load bundle %s: %v", bundleName, err)
logging.Infof("Downloading bundle: %s...", bundleName)
bundlePath, err = bundle.Download(preset, bundlePath)
bundlePath, err = bundle.Download(preset, bundlePath, enableBundleQuayFallback)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -283,7 +283,7 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
}

bundleName := bundle.GetBundleNameWithoutExtension(bundle.GetBundleNameFromURI(startConfig.BundlePath))
crcBundleMetadata, err := getCrcBundleInfo(startConfig.Preset, bundleName, startConfig.BundlePath)
crcBundleMetadata, err := getCrcBundleInfo(startConfig.Preset, bundleName, startConfig.BundlePath, startConfig.EnableBundleQuayFallback)
if err != nil {
return nil, errors.Wrap(err, "Error getting bundle metadata")
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/crc/machine/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ type StartConfig struct {

// Enable emergency login
EmergencyLogin bool

// Enable bundle quay fallback
EnableBundleQuayFallback bool
}

type ClusterConfig struct {
Expand Down
3 changes: 2 additions & 1 deletion pkg/crc/preflight/preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ func getPreflightChecksHelper(config crcConfig.Storage) []Check {
mode := crcConfig.GetNetworkMode(config)
bundlePath := config.Get(crcConfig.Bundle).AsString()
preset := crcConfig.GetPreset(config)
enableBundleQuayFallback := config.Get(crcConfig.EnableBundleQuayFallback).AsBool()
logging.Infof("Using bundle path %s", bundlePath)
return getPreflightChecks(experimentalFeatures, mode, bundlePath, preset)
return getPreflightChecks(experimentalFeatures, mode, bundlePath, preset, enableBundleQuayFallback)
}

// StartPreflightChecks performs the preflight checks before starting the cluster
Expand Down
8 changes: 4 additions & 4 deletions pkg/crc/preflight/preflight_checks_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import (
"github.com/pkg/errors"
)

func bundleCheck(bundlePath string, preset crcpreset.Preset) Check {
func bundleCheck(bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) Check {
return Check{
configKeySuffix: "check-bundle-extracted",
checkDescription: "Checking if CRC bundle is extracted in '$HOME/.crc'",
check: checkBundleExtracted(bundlePath),
fixDescription: "Getting bundle for the CRC executable",
fix: fixBundleExtracted(bundlePath, preset),
fix: fixBundleExtracted(bundlePath, preset, enableBundleQuayFallback),
flags: SetupOnly,

labels: None,
Expand Down Expand Up @@ -96,7 +96,7 @@ func checkBundleExtracted(bundlePath string) func() error {
}
}

func fixBundleExtracted(bundlePath string, preset crcpreset.Preset) func() error {
func fixBundleExtracted(bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) func() error {
// Should be removed after 1.19 release
// This check will ensure correct mode for `~/.crc/cache` directory
// in case it exists.
Expand All @@ -123,7 +123,7 @@ func fixBundleExtracted(bundlePath string, preset crcpreset.Preset) func() error

var err error
logging.Infof("Downloading bundle: %s...", bundlePath)
if bundlePath, err = bundle.Download(preset, bundlePath); err != nil {
if bundlePath, err = bundle.Download(preset, bundlePath, enableBundleQuayFallback); err != nil {
return err
}

Expand Down
10 changes: 5 additions & 5 deletions pkg/crc/preflight/preflight_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ var daemonLaunchdChecks = []Check{
// Passing 'SystemNetworkingMode' to getPreflightChecks currently achieves this
// as there are no user networking specific checks
func getAllPreflightChecks() []Check {
return getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift)
return getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false)
}

func getChecks(_ network.Mode, bundlePath string, preset crcpreset.Preset) []Check {
func getChecks(_ network.Mode, bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
checks := []Check{}

checks = append(checks, nonWinPreflightChecks...)
Expand All @@ -111,16 +111,16 @@ func getChecks(_ network.Mode, bundlePath string, preset crcpreset.Preset) []Che
checks = append(checks, genericCleanupChecks...)
checks = append(checks, vfkitPreflightChecks...)
checks = append(checks, resolverPreflightChecks...)
checks = append(checks, bundleCheck(bundlePath, preset))
checks = append(checks, bundleCheck(bundlePath, preset, enableBundleQuayFallback))
checks = append(checks, trayLaunchdCleanupChecks...)
checks = append(checks, daemonLaunchdChecks...)

return checks
}

func getPreflightChecks(_ bool, mode network.Mode, bundlePath string, preset crcpreset.Preset) []Check {
func getPreflightChecks(_ bool, mode network.Mode, bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
filter := newFilter()
filter.SetNetworkMode(mode)

return filter.Apply(getChecks(mode, bundlePath, preset))
return filter.Apply(getChecks(mode, bundlePath, preset, enableBundleQuayFallback))
}
8 changes: 4 additions & 4 deletions pkg/crc/preflight/preflight_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ func TestCountConfigurationOptions(t *testing.T) {
}

func TestCountPreflights(t *testing.T) {
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 17)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 17)

assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 16)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 16)
}
14 changes: 7 additions & 7 deletions pkg/crc/preflight/preflight_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,26 +343,26 @@ func getAllPreflightChecks() []Check {
filter.SetDistro(distro())
filter.SetSystemdUser(distro())

return filter.Apply(getChecks(distro(), constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift))
return filter.Apply(getChecks(distro(), constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false))
}

func getPreflightChecks(_ bool, networkMode network.Mode, bundlePath string, preset crcpreset.Preset) []Check {
func getPreflightChecks(_ bool, networkMode network.Mode, bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
usingSystemdResolved := checkSystemdResolvedIsRunning()

return getPreflightChecksForDistro(distro(), networkMode, usingSystemdResolved == nil, bundlePath, preset)
return getPreflightChecksForDistro(distro(), networkMode, usingSystemdResolved == nil, bundlePath, preset, enableBundleQuayFallback)
}

func getPreflightChecksForDistro(distro *linux.OsRelease, networkMode network.Mode, usingSystemdResolved bool, bundlePath string, preset crcpreset.Preset) []Check {
func getPreflightChecksForDistro(distro *linux.OsRelease, networkMode network.Mode, usingSystemdResolved bool, bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
filter := newFilter()
filter.SetDistro(distro)
filter.SetSystemdUser(distro)
filter.SetNetworkMode(networkMode)
filter.SetSystemdResolved(usingSystemdResolved)

return filter.Apply(getChecks(distro, bundlePath, preset))
return filter.Apply(getChecks(distro, bundlePath, preset, enableBundleQuayFallback))
}

func getChecks(distro *linux.OsRelease, bundlePath string, preset crcpreset.Preset) []Check {
func getChecks(distro *linux.OsRelease, bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
var checks []Check
checks = append(checks, nonWinPreflightChecks...)
checks = append(checks, wsl2PreflightCheck)
Expand All @@ -376,7 +376,7 @@ func getChecks(distro *linux.OsRelease, bundlePath string, preset crcpreset.Pres
checks = append(checks, dnsmasqPreflightChecks...)
checks = append(checks, libvirtNetworkPreflightChecks...)
checks = append(checks, vsockPreflightCheck)
checks = append(checks, bundleCheck(bundlePath, preset))
checks = append(checks, bundleCheck(bundlePath, preset, enableBundleQuayFallback))

return checks
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/crc/preflight/preflight_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ func assertFuncEqual(t *testing.T, func1 interface{}, func2 interface{}) {
}

func assertExpectedPreflights(t *testing.T, distro *crcos.OsRelease, networkMode network.Mode, systemdResolved bool) {
preflights := getPreflightChecksForDistro(distro, networkMode, systemdResolved, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift)
preflights := getPreflightChecksForDistro(distro, networkMode, systemdResolved, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false)
var expected checkListForDistro
for _, expected = range checkListForDistros {
if expected.distro == distro && expected.networkMode == networkMode && expected.systemdResolved == systemdResolved {
Expand Down
10 changes: 5 additions & 5 deletions pkg/crc/preflight/preflight_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,27 +200,27 @@ func checkVsock() error {
// Passing 'UserNetworkingMode' to getPreflightChecks currently achieves this
// as there are no system networking specific checks
func getAllPreflightChecks() []Check {
return getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift)
return getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false)
}

func getChecks(bundlePath string, preset crcpreset.Preset) []Check {
func getChecks(bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
checks := []Check{}
checks = append(checks, memoryCheck(preset))
checks = append(checks, hypervPreflightChecks...)
checks = append(checks, crcUsersGroupExistsCheck)
checks = append(checks, userPartOfCrcUsersAndHypervAdminsGroupCheck)
checks = append(checks, vsockChecks...)
checks = append(checks, bundleCheck(bundlePath, preset))
checks = append(checks, bundleCheck(bundlePath, preset, enableBundleQuayFallback))
checks = append(checks, genericCleanupChecks...)
checks = append(checks, cleanupCheckRemoveCrcVM)
checks = append(checks, daemonTaskChecks...)
checks = append(checks, adminHelperServiceCheks...)
return checks
}

func getPreflightChecks(_ bool, networkMode network.Mode, bundlePath string, preset crcpreset.Preset) []Check {
func getPreflightChecks(_ bool, networkMode network.Mode, bundlePath string, preset crcpreset.Preset, enableBundleQuayFallback bool) []Check {
filter := newFilter()
filter.SetNetworkMode(networkMode)

return filter.Apply(getChecks(bundlePath, preset))
return filter.Apply(getChecks(bundlePath, preset, enableBundleQuayFallback))
}
8 changes: 4 additions & 4 deletions pkg/crc/preflight/preflight_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ func TestCountConfigurationOptions(t *testing.T) {
}

func TestCountPreflights(t *testing.T) {
assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 21)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 21)
assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 21)
assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 21)

assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 20)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 20)
assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 20)
assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 20)
}

0 comments on commit 504ffef

Please sign in to comment.