Skip to content

Commit

Permalink
Merge pull request #2590 from replicatedhq/divolgin/airgap
Browse files Browse the repository at this point in the history
Don't allow kots upgrades in airgap and kurl installs
  • Loading branch information
divolgin committed Mar 7, 2022
2 parents f64c96a + d871b8f commit 81abec0
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 57 deletions.
5 changes: 1 addition & 4 deletions pkg/automation/automation.go
Expand Up @@ -96,10 +96,7 @@ LICENSE_LOOP:
continue
}

disableOutboundConnections := false
// ignore the error, default to false
disableOutboundConnections, _ = strconv.ParseBool(os.Getenv("DISABLE_OUTBOUND_CONNECTIONS"))
if !disableOutboundConnections {
if !kotsadm.IsAirgap() {
licenseData, err := kotslicense.GetLatestLicense(verifiedLicense)
if err != nil {
logger.Error(errors.Wrap(err, "failed to get latest license"))
Expand Down
8 changes: 2 additions & 6 deletions pkg/handlers/license.go
Expand Up @@ -5,14 +5,13 @@ import (
"encoding/json"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"time"

"github.com/gorilla/mux"
"github.com/pkg/errors"
kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
"github.com/replicatedhq/kots/pkg/kotsadm"
kotsadmtypes "github.com/replicatedhq/kots/pkg/kotsadm/types"
license "github.com/replicatedhq/kots/pkg/kotsadmlicense"
"github.com/replicatedhq/kots/pkg/kotsutil"
Expand Down Expand Up @@ -273,10 +272,7 @@ func (h *Handler) UploadNewLicense(w http.ResponseWriter, r *http.Request) {
return
}

disableOutboundConnections := false
// ignore the error, default to false
disableOutboundConnections, _ = strconv.ParseBool(os.Getenv("DISABLE_OUTBOUND_CONNECTIONS"))
if !disableOutboundConnections {
if !kotsadm.IsAirgap() {
// sync license
logger.Info("syncing license with server to retrieve latest version")
licenseData, err := kotslicense.GetLatestLicense(verifiedLicense)
Expand Down
39 changes: 24 additions & 15 deletions pkg/handlers/metadata.go
Expand Up @@ -7,7 +7,8 @@ import (

kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/kurl"
"github.com/replicatedhq/kots/pkg/kotsadm"
"github.com/replicatedhq/kots/pkg/kotsadm/types"
"github.com/replicatedhq/kots/pkg/logger"
"github.com/replicatedhq/kots/pkg/util"
v1 "k8s.io/api/core/v1"
Expand All @@ -26,13 +27,18 @@ const (

// MetadataResponse non sensitive information to be used by ui pre-login
type MetadataResponse struct {
IconURI string `json:"iconUri"`
Name string `json:"name"`
Namespace string `json:"namespace"`
IsKurlEnabled bool `json:"isKurlEnabled"`
UpstreamURI string `json:"upstreamUri"`
IconURI string `json:"iconUri"`
Name string `json:"name"`
Namespace string `json:"namespace"`
UpstreamURI string `json:"upstreamUri"`
// ConsoleFeatureFlags optional flags from application.yaml used to enable ui features
ConsoleFeatureFlags []string `json:"consoleFeatureFlags"`
ConsoleFeatureFlags []string `json:"consoleFeatureFlags"`
AdminConsoleMetadata AdminConsoleMetadata `json:"adminConsoleMetadata"`
}

type AdminConsoleMetadata struct {
IsAirgap bool `json:"isAirgap"`
IsKurl bool `json:"isKurl"`
}

// GetMetadataHandler helper function that returns a http handler func that returns metadata. It takes a function that
Expand All @@ -45,7 +51,7 @@ func GetMetadataHandler(getK8sInfoFn MetadataK8sFn) http.HandlerFunc {
Namespace: util.PodNamespace,
}

brandingConfigMap, isKurlEnabled, err := getK8sInfoFn()
brandingConfigMap, kotsadmMetadata, err := getK8sInfoFn()
if err != nil {
// if we can't find config map in cluster, it's not an error, we still want to return a stripped down response
if kuberneteserrors.IsNotFound(err) {
Expand Down Expand Up @@ -80,31 +86,34 @@ func GetMetadataHandler(getK8sInfoFn MetadataK8sFn) http.HandlerFunc {
return
}
application := obj.(*kotsv1beta1.Application)
metadataResponse.IsKurlEnabled = isKurlEnabled
metadataResponse.IconURI = application.Spec.Icon
metadataResponse.Name = application.Spec.Title
metadataResponse.UpstreamURI = brandingConfigMap.Data[upstreamUriKey]
metadataResponse.ConsoleFeatureFlags = application.Spec.ConsoleFeatureFlags
metadataResponse.AdminConsoleMetadata = AdminConsoleMetadata{
IsAirgap: kotsadmMetadata.IsAirgap,
IsKurl: kotsadmMetadata.IsKurl,
}

JSON(w, http.StatusOK, metadataResponse)
}
}

// GetMetaDataConfig retrieves configMap from k8s used to construct metadata
func GetMetaDataConfig() (*v1.ConfigMap, bool, error) {
func GetMetaDataConfig() (*v1.ConfigMap, types.Metadata, error) {
clientset, err := k8sutil.GetClientset()
if err != nil {
return nil, false, nil
return nil, types.Metadata{}, nil
}

isKurlEnabled := kurl.IsKurl()
kotsadmMetadata := kotsadm.GetMetadata()

brandingConfigMap, err := clientset.CoreV1().ConfigMaps(util.PodNamespace).Get(context.TODO(), metadataConfigMapName, metav1.GetOptions{})
if err != nil {
return nil, false, err
return nil, kotsadmMetadata, err
}

return brandingConfigMap, isKurlEnabled, nil
return brandingConfigMap, kotsadmMetadata, nil
}

type MetadataK8sFn func() (*v1.ConfigMap, bool, error)
type MetadataK8sFn func() (*v1.ConfigMap, types.Metadata, error)
20 changes: 13 additions & 7 deletions pkg/handlers/metadata_test.go
Expand Up @@ -7,6 +7,7 @@ import (
"net/http/httptest"
"testing"

"github.com/replicatedhq/kots/pkg/kotsadm/types"
"github.com/replicatedhq/kots/pkg/util"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -51,17 +52,22 @@ metadata:
}{
{
name: "happy path feature flag test",
funcPtr: func() (*v1.ConfigMap, bool, error) {
funcPtr: func() (*v1.ConfigMap, types.Metadata, error) {

// parse data as a kotskind
obj, _, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(configMap), nil, nil)
require.Nil(t, err)

return obj.(*v1.ConfigMap), true, nil
meta := types.Metadata{
IsKurl: true,
}
return obj.(*v1.ConfigMap), meta, nil

},
expected: MetadataResponse{
IsKurlEnabled: true,
AdminConsoleMetadata: AdminConsoleMetadata{
IsKurl: true,
},
IconURI: "https://foo.com/icon.png",
Name: "App Name",
ConsoleFeatureFlags: []string{"feature1", "feature2"},
Expand All @@ -71,15 +77,15 @@ metadata:
},
{
name: "cluster error",
funcPtr: func() (*v1.ConfigMap, bool, error) {
return nil, false, errors.New("wah wah wah")
funcPtr: func() (*v1.ConfigMap, types.Metadata, error) {
return nil, types.Metadata{}, errors.New("wah wah wah")
},
httpStatus: http.StatusServiceUnavailable,
},
{
name: "cluster present, no kurl",
funcPtr: func() (*v1.ConfigMap, bool, error) {
return nil, false, &mockNotFound{}
funcPtr: func() (*v1.ConfigMap, types.Metadata, error) {
return nil, types.Metadata{}, &mockNotFound{}
},
httpStatus: http.StatusOK,
expected: MetadataResponse{
Expand Down
21 changes: 15 additions & 6 deletions pkg/handlers/update.go
Expand Up @@ -217,19 +217,32 @@ func (h *Handler) UpdateAdminConsole(w http.ResponseWriter, r *http.Request) {
Success: false,
}

isKurl, err := kotsadm.IsKurl()
if err != nil {
logger.Error(errors.Wrap(err, "failed to check kURL"))
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}

if isKurl || kotsadm.IsAirgap() {
err := errors.New("cannot automatically update the admin console in kURL or airgapped installations")
logger.Error(err)
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusBadRequest, updateAdminConsoleResponse)
return
}

appSlug := mux.Vars(r)["appSlug"]
sequence, err := strconv.Atoi(mux.Vars(r)["sequence"])
if err != nil {
logger.Error(errors.Wrap(err, "failed to decode UpdateAdminConsole request body"))
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}

status, _, err := kotsadm.GetKotsUpdateStatus()
if err != nil {
logger.Error(errors.Wrap(err, "failed to check update status"))
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}
Expand All @@ -245,7 +258,6 @@ func (h *Handler) UpdateAdminConsole(w http.ResponseWriter, r *http.Request) {
a, err := store.GetStore().GetAppFromSlug(appSlug)
if err != nil {
logger.Error(errors.Wrap(err, "failed to get app from slug"))
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}
Expand All @@ -254,7 +266,6 @@ func (h *Handler) UpdateAdminConsole(w http.ResponseWriter, r *http.Request) {
version, err := store.GetStore().GetAppVersion(a.ID, int64(sequence))
if err != nil {
logger.Error(errors.Wrap(err, "failed to get app version"))
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}
Expand All @@ -264,7 +275,6 @@ func (h *Handler) UpdateAdminConsole(w http.ResponseWriter, r *http.Request) {
targetVersion, err := getKotsUpgradeVersion(version.KOTSKinds, latestVersion)
if err != nil {
logger.Error(errors.Wrap(err, "failed to find target version"))
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}
Expand All @@ -274,7 +284,6 @@ func (h *Handler) UpdateAdminConsole(w http.ResponseWriter, r *http.Request) {
err = kotsadm.UpdateToVersion(targetVersion)
if err != nil {
logger.Error(errors.Wrap(err, "failed to check update status"))
updateAdminConsoleResponse.Error = err.Error()
JSON(w, http.StatusInternalServerError, updateAdminConsoleResponse)
return
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/kotsadm/main.go
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -258,6 +259,10 @@ func IsKurl() (bool, error) {
return kotsutil.IsKurl(clientset), nil
}

func IsAirgap() bool {
return os.Getenv("DISABLE_OUTBOUND_CONNECTIONS") == "true"
}

func canUpgrade(upgradeOptions types.UpgradeOptions, clientset *kubernetes.Clientset, log *logger.CLILogger) error {
_, err := clientset.CoreV1().Namespaces().Get(context.TODO(), upgradeOptions.Namespace, metav1.GetOptions{})
if kuberneteserrors.IsNotFound(err) {
Expand Down
15 changes: 15 additions & 0 deletions pkg/kotsadm/metadata.go
@@ -0,0 +1,15 @@
package kotsadm

import (
"github.com/replicatedhq/kots/pkg/kotsadm/types"
"github.com/replicatedhq/kots/pkg/kurl"
)

func GetMetadata() types.Metadata {
metadata := types.Metadata{
IsAirgap: IsAirgap(),
IsKurl: kurl.IsKurl(),
}

return metadata
}
6 changes: 6 additions & 0 deletions pkg/kotsadm/types/metadata.go
@@ -0,0 +1,6 @@
package types

type Metadata struct {
IsAirgap bool
IsKurl bool
}
7 changes: 2 additions & 5 deletions pkg/reporting/util.go
Expand Up @@ -5,9 +5,9 @@ import (
"net/http"
"os"
"regexp"
"strconv"

"github.com/replicatedhq/kots/pkg/api/reporting/types"
"github.com/replicatedhq/kots/pkg/kotsadm"
)

func InjectReportingInfoHeaders(req *http.Request, reportingInfo *types.ReportingInfo) {
Expand Down Expand Up @@ -41,10 +41,7 @@ func InjectReportingInfoHeaders(req *http.Request, reportingInfo *types.Reportin
}

func canReport(endpoint string) bool {
disableOutboundConnections := false
// ignore the error, default to false
disableOutboundConnections, _ = strconv.ParseBool(os.Getenv("DISABLE_OUTBOUND_CONNECTIONS"))
if disableOutboundConnections {
if kotsadm.IsAirgap() {
return false
}
if os.Getenv("KOTSADM_ENV") == "dev" && !isDevEndpoint(endpoint) {
Expand Down
10 changes: 5 additions & 5 deletions web/src/Root.jsx
Expand Up @@ -209,7 +209,7 @@ class Root extends PureComponent {
selectedAppName: data.name,
appSlugFromMetadata: parseUpstreamUri(data.upstreamUri),
appNameSpace: data.namespace,
isKurlEnabled: data.isKurlEnabled,
adminConsoleMetadata: data.adminConsoleMetadata,
featureFlags: data.consoleFeatureFlags,
fetchingMetadata: false
});
Expand Down Expand Up @@ -338,7 +338,7 @@ class Root extends PureComponent {
logo={themeState.navbarLogo || this.state.appLogo}
refetchAppsList={this.getAppsList}
fetchingMetadata={this.state.fetchingMetadata}
isKurlEnabled={this.state.isKurlEnabled}
isKurlEnabled={this.state.adminConsoleMetadata?.isKurl}
isGitOpsSupported={this.isGitOpsSupported()}
isIdentityServiceSupported={this.isIdentityServiceSupported()}
appsList={appsList}
Expand Down Expand Up @@ -367,16 +367,16 @@ class Root extends PureComponent {
<Route path="/unsupported" component={UnsupportedBrowser} />
<ProtectedRoute path="/cluster/manage" render={(props) => <ClusterNodes {...props} appName={this.state.selectedAppName} />} />
<ProtectedRoute path="/gitops" render={(props) => <GitOps {...props} appName={this.state.selectedAppName} />} />
<ProtectedRoute path="/access/:tab?" render={(props) => <Access {...props} appName={this.state.selectedAppName} isKurlEnabled={this.state.isKurlEnabled} isGeoaxisSupported={this.isGeoaxisSupported()} />} />
<ProtectedRoute path="/access/:tab?" render={(props) => <Access {...props} appName={this.state.selectedAppName} isKurlEnabled={this.state.adminConsoleMetadata?.isKurl} isGeoaxisSupported={this.isGeoaxisSupported()} />} />
<ProtectedRoute
path={["/snapshots/:tab?"]}
render={
props => (
<SnapshotsWrapper
{...props}
appName={this.state.selectedAppName}
isKurlEnabled={this.state.isKurlEnabled}
appsList={appsList}
isKurlEnabled={this.state.adminConsoleMetadata?.isKurl}
nabled={this.state.adminConsoleMetadata.isKurl} appsList={appsList}
/>
)
}
Expand Down
1 change: 1 addition & 0 deletions web/src/components/apps/AppDetailPage.jsx
Expand Up @@ -378,6 +378,7 @@ class AppDetailPage extends Component {
makingCurrentRelease={this.state.makingCurrentRelease}
redeployVersion={this.redeployVersion}
redeployVersionErrMsg={this.state.redeployVersionErrMsg}
adminConsoleMetadata={this.props.adminConsoleMetadata}
/>}
/>
<Route exact path="/app/:slug/downstreams/:downstreamSlug/version-history/preflight/:sequence" render={props => <PreflightResultPage logo={app.iconUri} {...props} />} />
Expand Down
1 change: 1 addition & 0 deletions web/src/components/apps/AppVersionHistory.jsx
Expand Up @@ -1085,6 +1085,7 @@ class AppVersionHistory extends Component {
handleSelectReleasesToDiff={this.handleSelectReleasesToDiff}
renderVersionDownloadStatus={this.renderVersionDownloadStatus}
isDownloading={this.state.versionDownloadStatuses?.[version.sequence]?.downloadingVersion}
adminConsoleMetadata={this.props.adminConsoleMetadata}
/>
);
}
Expand Down

0 comments on commit 81abec0

Please sign in to comment.