Repository: replicatedhq/kots
Pull request #133 (2019-11-08 01:28:22)

Syncing license data


Patch:
diff --git a/ffi/config.go b/ffi/config.go
index b1fa46d7c..5235ab527 100644
--- a/ffi/config.go
+++ b/ffi/config.go
@@ -6,11 +6,12 @@ import (
 	"fmt"
 
 	"github.com/replicatedhq/kots/pkg/config"
+	"github.com/replicatedhq/kots/pkg/logger"
 )
 
 //export TemplateConfig
 func TemplateConfig(configPath string, configData string, configValuesData string) *C.char {
-	rendered, err := config.TemplateConfig(configPath, configData, configValuesData)
+	rendered, err := config.TemplateConfig(logger.NewLogger(), configPath, configData, configValuesData)
 	if err != nil {
 		fmt.Printf("failed to apply templates to config: %s\n", err.Error())
 		return C.CString("")
diff --git a/ffi/main.go b/ffi/main.go
index ef3b6c71f..9ce3b068d 100644
--- a/ffi/main.go
+++ b/ffi/main.go
@@ -3,8 +3,10 @@ package main
 import "C"
 
 import (
+	"encoding/base64"
 	"fmt"
 	"io/ioutil"
+	"net/http"
 	"os"
 	"path/filepath"
 
@@ -13,6 +15,7 @@ import (
 	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
 	kotsscheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
 	"github.com/replicatedhq/kots/pkg/pull"
+	"github.com/replicatedhq/kots/pkg/upstream"
 	"k8s.io/client-go/kubernetes/scheme"
 )
 
@@ -134,6 +137,94 @@ func UpdateCheck(socket, fromArchivePath string) {
 	}()
 }
 
+//export GetLatestLicense
+func GetLatestLicense(socket, licenseData string) {
+	go func() {
+		var ffiResult *FFIResult
+
+		statusClient, err := connectToStatusServer(socket)
+		if err != nil {
+			fmt.Printf("failed to connect to status server: %s\n", err)
+			return
+		}
+		defer func() {
+			statusClient.end(ffiResult)
+		}()
+
+		kotsscheme.AddToScheme(scheme.Scheme)
+		decode := scheme.Codecs.UniversalDeserializer().Decode
+		obj, _, err := decode([]byte(licenseData), nil, nil)
+		if err != nil {
+			fmt.Printf("failed to decode license data: %s\n", err.Error())
+			ffiResult = NewFFIResult(-1).WithError(err)
+			return
+		}
+		license := obj.(*kotsv1beta1.License)
+
+		url := fmt.Sprintf("%s/release/%s/license", license.Spec.Endpoint, license.Spec.AppSlug)
+
+		req, err := http.NewRequest("GET", url, nil)
+		if err != nil {
+			fmt.Printf("failed to call newrequest: %s\n", err.Error())
+			ffiResult = NewFFIResult(-1).WithError(err)
+			return
+		}
+
+		req.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", license.Spec.LicenseID, license.Spec.LicenseID)))))
+
+		resp, err := http.DefaultClient.Do(req)
+		if err != nil {
+			fmt.Printf("failed to execute get request: %s\n", err.Error())
+			ffiResult = NewFFIResult(-1).WithError(err)
+			return
+		}
+		defer resp.Body.Close()
+
+		if resp.StatusCode >= 400 {
+			fmt.Printf("unexpected result from get request: %d\n", resp.StatusCode)
+			ffiResult = NewFFIResult(-1).WithError(err)
+			return
+		}
+
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			fmt.Printf("failed to load response")
+			ffiResult = NewFFIResult(-1).WithError(err)
+			return
+		}
+
+		obj, _, err = decode(body, nil, nil)
+		if err != nil {
+			fmt.Printf("failed to decode latest license data: %s\n", err.Error())
+			ffiResult = NewFFIResult(-1).WithError(err)
+			return
+		}
+		latestLicense := obj.(*kotsv1beta1.License)
+
+		marshalledLicense := upstream.MustMarshalLicense(latestLicense)
+		ffiResult = NewFFIResult(1).WithData(string(marshalledLicense))
+	}()
+}
+
+//export VerifyAirgapLicense
+func VerifyAirgapLicense(licenseData string) *C.char {
+	kotsscheme.AddToScheme(scheme.Scheme)
+	decode := scheme.Codecs.UniversalDeserializer().Decode
+	obj, _, err := decode([]byte(licenseData), nil, nil)
+	if err != nil {
+		fmt.Printf("failed to decode license data: %s\n", err.Error())
+		return nil
+	}
+	license := obj.(*kotsv1beta1.License)
+
+	if err := pull.VerifySignature(license); err != nil {
+		fmt.Printf("failed to verify airgap license signature: %s\n", err.Error())
+		return nil
+	}
+
+	return C.CString("verified")
+}
+
 func readCursorFromPath(rootPath string) (string, error) {
 	installationFilePath := filepath.Join(rootPath, "upstream", "userdata", "installation.yaml")
 	_, err := os.Stat(installationFilePath)
diff --git a/kotskinds/apis/kots/v1beta1/license_types.go b/kotskinds/apis/kots/v1beta1/license_types.go
index f6279c0bb..ab7e7eb88 100644
--- a/kotskinds/apis/kots/v1beta1/license_types.go
+++ b/kotskinds/apis/kots/v1beta1/license_types.go
@@ -102,6 +102,7 @@ type LicenseSpec struct {
 	AppSlug           string                      `json:"appSlug"`
 	Endpoint          string                      `json:"endpoint,omitempty"`
 	ChannelName       string                      `json:"channelName,omitempty"`
+	LicenseSequence   int64                       `json:"licenseSequence,omitempty"`
 	LicenseID         string                      `json:"licenseID"`
 	IsAirgapSupported bool                        `json:"isAirgapSupported,omitempty"`
 	Entitlements      map[string]EntitlementField `json:"entitlements,omitempty"`
diff --git a/pkg/base/render.go b/pkg/base/render.go
index 2d5e18c16..38b47d09a 100644
--- a/pkg/base/render.go
+++ b/pkg/base/render.go
@@ -2,6 +2,7 @@ package base
 
 import (
 	"github.com/pkg/errors"
+	"github.com/replicatedhq/kots/pkg/logger"
 	"github.com/replicatedhq/kots/pkg/upstream"
 )
 
@@ -9,6 +10,7 @@ type RenderOptions struct {
 	SplitMultiDocYAML bool
 	Namespace         string
 	HelmOptions       []string
+	Log               *logger.Logger
 }
 
 // RenderUpstream is responsible for any conversions or transpilation steps are required
diff --git a/pkg/base/replicated.go b/pkg/base/replicated.go
index 8df24aacb..46937c762 100644
--- a/pkg/base/replicated.go
+++ b/pkg/base/replicated.go
@@ -25,10 +25,11 @@ func renderReplicated(u *upstream.Upstream, renderOptions *RenderOptions) (*Base
 		if c.Path == "userdata/config.yaml" {
 			ctx, err := UnmarshalConfigValuesContent(c.Content)
 			if err != nil {
-				return nil, errors.Wrap(err, "failed to unmarshal config values content")
+				renderOptions.Log.Error(err)
+				templateContext = map[string]template.ItemValue{}
+			} else {
+				templateContext = ctx
 			}
-
-			templateContext = ctx
 		}
 	}
 
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 66e0d6215..ef4f56b38 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -7,12 +7,13 @@ import (
 	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
 	kotsscheme "github.com/replicatedhq/kots/kotskinds/client/kotsclientset/scheme"
 	"github.com/replicatedhq/kots/pkg/base"
+	"github.com/replicatedhq/kots/pkg/logger"
 	"github.com/replicatedhq/kots/pkg/template"
 	"k8s.io/apimachinery/pkg/runtime/serializer/json"
 	"k8s.io/client-go/kubernetes/scheme"
 )
 
-func TemplateConfig(configPath string, configData string, configValuesData string) (string, error) {
+func TemplateConfig(log *logger.Logger, configPath string, configData string, configValuesData string) (string, error) {
 	// This function will
 	// 1. unmarshal config
 	// 2. replace all item values with values that already exist
@@ -34,7 +35,8 @@ func TemplateConfig(configPath string, configData string, configValuesData strin
 	// get template context from config values
 	templateContext, err := base.UnmarshalConfigValuesContent([]byte(configValuesData))
 	if err != nil {
-		return "", errors.Wrap(err, "failed to unmarshal config values")
+		log.Error(err)
+		templateContext = map[string]template.ItemValue{}
 	}
 
 	// add config context
diff --git a/pkg/kotsadm/api_objects.go b/pkg/kotsadm/api_objects.go
index a45d2ccc7..3483b8896 100644
--- a/pkg/kotsadm/api_objects.go
+++ b/pkg/kotsadm/api_objects.go
@@ -8,6 +8,8 @@ import (
 	rbacv1 "k8s.io/api/rbac/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
+
+	"github.com/replicatedhq/kots/pkg/util"
 )
 
 func apiRole(namespace string) *rbacv1.Role {
@@ -98,6 +100,9 @@ func apiDeployment(namespace, autoCreateClusterToken string) *appsv1.Deployment
 					},
 				},
 				Spec: corev1.PodSpec{
+					SecurityContext: &corev1.PodSecurityContext{
+						RunAsUser: util.IntPointer(1001),
+					},
 					ServiceAccountName: "kotsadm-api",
 					RestartPolicy:      corev1.RestartPolicyAlways,
 					Containers: []corev1.Container{
diff --git a/pkg/kotsadm/minio_objects.go b/pkg/kotsadm/minio_objects.go
index 19d07c361..73c5ce8c3 100644
--- a/pkg/kotsadm/minio_objects.go
+++ b/pkg/kotsadm/minio_objects.go
@@ -146,6 +146,9 @@ func minioStatefulset(namespace string) *appsv1.StatefulSet {
 					},
 				},
 				Spec: corev1.PodSpec{
+					SecurityContext: &corev1.PodSecurityContext{
+						// RunAsUser: util.IntPointer(1001), // TODO: make real user #
+					},
 					Volumes: []corev1.Volume{
 						{
 							Name: "kotsadm-minio",
@@ -300,6 +303,9 @@ func minioJob(namespace string) *batchv1.Job {
 					},
 				},
 				Spec: corev1.PodSpec{
+					SecurityContext: &corev1.PodSecurityContext{
+						// RunAsUser: util.IntPointer(1001), // TODO: make real user #
+					},
 					RestartPolicy: corev1.RestartPolicyOnFailure,
 					Volumes: []corev1.Volume{
 						{
diff --git a/pkg/kotsadm/operator_objects.go b/pkg/kotsadm/operator_objects.go
index 621d6737f..a942e5f00 100644
--- a/pkg/kotsadm/operator_objects.go
+++ b/pkg/kotsadm/operator_objects.go
@@ -7,6 +7,8 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/replicatedhq/kots/pkg/util"
 )
 
 func operatorRole(namespace string) *rbacv1.Role {
@@ -96,6 +98,9 @@ func operatorDeployment(namespace, autoCreateClusterToken string) *appsv1.Deploy
 					},
 				},
 				Spec: corev1.PodSpec{
+					SecurityContext: &corev1.PodSecurityContext{
+						RunAsUser: util.IntPointer(1001),
+					},
 					ServiceAccountName: "kotsadm-operator",
 					RestartPolicy:      corev1.RestartPolicyAlways,
 					Containers: []corev1.Container{
diff --git a/pkg/kotsadm/postgres_objects.go b/pkg/kotsadm/postgres_objects.go
index 114b28b5c..07dc6cd5f 100644
--- a/pkg/kotsadm/postgres_objects.go
+++ b/pkg/kotsadm/postgres_objects.go
@@ -48,6 +48,9 @@ func postgresStatefulset(namespace string) *appsv1.StatefulSet {
 					},
 				},
 				Spec: corev1.PodSpec{
+					SecurityContext: &corev1.PodSecurityContext{
+						// RunAsUser: util.IntPointer(1001), // TODO: make real user #
+					},
 					Volumes: []corev1.Volume{
 						{
 							Name: "kotsadm-postgres",
diff --git a/pkg/kotsadm/schemahero_objects.go b/pkg/kotsadm/schemahero_objects.go
index 5efa5e095..526373a18 100644
--- a/pkg/kotsadm/schemahero_objects.go
+++ b/pkg/kotsadm/schemahero_objects.go
@@ -6,6 +6,8 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/replicatedhq/kots/pkg/util"
 )
 
 func migrationsPod(deployOptions DeployOptions) *corev1.Pod {
@@ -21,6 +23,10 @@ func migrationsPod(deployOptions DeployOptions) *corev1.Pod {
 			Namespace: deployOptions.Namespace,
 		},
 		Spec: corev1.PodSpec{
+			SecurityContext: &corev1.PodSecurityContext{
+				RunAsUser: util.IntPointer(1001),
+				FSGroup:   util.IntPointer(1001),
+			},
 			RestartPolicy: corev1.RestartPolicyOnFailure,
 			Containers: []corev1.Container{
 				{
diff --git a/pkg/kotsadm/web.go b/pkg/kotsadm/web.go
index 27a62abbf..591117a39 100644
--- a/pkg/kotsadm/web.go
+++ b/pkg/kotsadm/web.go
@@ -17,7 +17,7 @@ import (
 )
 
 var (
-	executableMode = int32(484)
+	executableMode = int32(511) // hex 777
 )
 
 func getWebYAML(deployOptions DeployOptions) (map[string][]byte, error) {
diff --git a/pkg/kotsadm/web_objects.go b/pkg/kotsadm/web_objects.go
index f94ae46ca..8e4393910 100644
--- a/pkg/kotsadm/web_objects.go
+++ b/pkg/kotsadm/web_objects.go
@@ -7,6 +7,8 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
+
+	"github.com/replicatedhq/kots/pkg/util"
 )
 
 func webConfig(deployOptions DeployOptions) *corev1.ConfigMap {
@@ -64,6 +66,10 @@ func webDeployment(namespace string) *appsv1.Deployment {
 					},
 				},
 				Spec: corev1.PodSpec{
+					SecurityContext: &corev1.PodSecurityContext{
+						RunAsUser: util.IntPointer(101),
+						FSGroup:   util.IntPointer(101),
+					},
 					Volumes: []corev1.Volume{
 						{
 							Name: "kotsadm-web-scripts",
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index 38dc8f0c8..9ef389f09 100644
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -20,11 +20,14 @@ func NewLogger() *Logger {
 }
 
 func (l *Logger) Silence() {
+	if l == nil {
+		return
+	}
 	l.isSilent = true
 }
 
 func (l *Logger) Initialize() {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -32,7 +35,7 @@ func (l *Logger) Initialize() {
 }
 
 func (l *Logger) Finish() {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -40,7 +43,7 @@ func (l *Logger) Finish() {
 }
 
 func (l *Logger) Info(msg string, args ...interface{}) {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -50,7 +53,7 @@ func (l *Logger) Info(msg string, args ...interface{}) {
 }
 
 func (l *Logger) ActionWithoutSpinner(msg string, args ...interface{}) {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -64,7 +67,7 @@ func (l *Logger) ActionWithoutSpinner(msg string, args ...interface{}) {
 }
 
 func (l *Logger) ChildActionWithoutSpinner(msg string, args ...interface{}) {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -73,7 +76,7 @@ func (l *Logger) ChildActionWithoutSpinner(msg string, args ...interface{}) {
 }
 
 func (l *Logger) ActionWithSpinner(msg string, args ...interface{}) {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -103,7 +106,7 @@ func (l *Logger) ActionWithSpinner(msg string, args ...interface{}) {
 }
 
 func (l *Logger) ChildActionWithSpinner(msg string, args ...interface{}) {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -133,7 +136,7 @@ func (l *Logger) ChildActionWithSpinner(msg string, args ...interface{}) {
 }
 
 func (l *Logger) FinishChildSpinner() {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -150,7 +153,7 @@ func (l *Logger) FinishChildSpinner() {
 }
 
 func (l *Logger) FinishSpinner() {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -167,7 +170,7 @@ func (l *Logger) FinishSpinner() {
 }
 
 func (l *Logger) FinishSpinnerWithError() {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
@@ -184,7 +187,7 @@ func (l *Logger) FinishSpinnerWithError() {
 }
 
 func (l *Logger) Error(err error) {
-	if l.isSilent {
+	if l == nil || l.isSilent {
 		return
 	}
 
diff --git a/pkg/pull/pull.go b/pkg/pull/pull.go
index df72e249b..0e8b12c01 100644
--- a/pkg/pull/pull.go
+++ b/pkg/pull/pull.go
@@ -283,6 +283,7 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) {
 		SplitMultiDocYAML: true,
 		Namespace:         pullOptions.Namespace,
 		HelmOptions:       pullOptions.HelmOptions,
+		Log:               log,
 	}
 	log.ActionWithSpinner("Creating base")
 	b, err := base.RenderUpstream(u, &renderOptions)
@@ -362,7 +363,7 @@ func parseLicenseFromFile(filename string) (*kotsv1beta1.License, error) {
 	}
 
 	license := decoded.(*kotsv1beta1.License)
-	if err := verifySignature(license); err != nil {
+	if err := VerifySignature(license); err != nil {
 		return nil, errors.Wrap(err, "failed to verify signature")
 	}
 
diff --git a/pkg/pull/signature.go b/pkg/pull/signature.go
index 7c3b1f064..5523eaa75 100644
--- a/pkg/pull/signature.go
+++ b/pkg/pull/signature.go
@@ -28,7 +28,7 @@ type KeySignature struct {
 	GlobalKeyId string `json:"globalKeyId"`
 }
 
-func verifySignature(license *kotsv1beta1.License) error {
+func VerifySignature(license *kotsv1beta1.License) error {
 	signature := &Signature{}
 	if err := json.Unmarshal(license.Spec.Signature, signature); err != nil {
 		// old licenses's signature is a single space character
@@ -101,6 +101,10 @@ func getMessageFromLicense(license *kotsv1beta1.License) ([]byte, error) {
 		"spec.isAirgapSupported": fmt.Sprintf("%t", license.Spec.IsAirgapSupported),
 	}
 
+	if license.Spec.LicenseSequence > 0 {
+		fields["spec.licenseSequence"] = fmt.Sprintf("%d", license.Spec.LicenseSequence)
+	}
+
 	for k, v := range license.Spec.Entitlements {
 		key := fmt.Sprintf("spec.entitlements.%s", k)
 		val := map[string]string{
diff --git a/pkg/upstream/replicated.go b/pkg/upstream/replicated.go
index 4d0a0c1b9..e523db02d 100644
--- a/pkg/upstream/replicated.go
+++ b/pkg/upstream/replicated.go
@@ -131,7 +131,7 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 
 	// Add the license to the upstream, if one was propvided
 	if license != nil {
-		release.Manifests["userdata/license.yaml"] = mustMarshalLicense(license)
+		release.Manifests["userdata/license.yaml"] = MustMarshalLicense(license)
 	}
 
 	files, err := releaseToFiles(release)
@@ -320,7 +320,7 @@ func downloadReplicatedApp(replicatedUpstream *ReplicatedUpstream, license *kots
 	return &release, nil
 }
 
-func mustMarshalLicense(license *kotsv1beta1.License) []byte {
+func MustMarshalLicense(license *kotsv1beta1.License) []byte {
 	kotsscheme.AddToScheme(scheme.Scheme)
 
 	s := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, scheme.Scheme)
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 57d24c019..c926365b9 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -54,3 +54,9 @@ func SplitStringOnLen(str string, maxLength int) ([]string, error) {
 
 	return result, nil
 }
+
+func IntPointer(x int) *int64 {
+	var xout int64
+	xout = int64(x)
+	return &xout
+}


Test patch:
diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go
index a781b3d2c..f271adb19 100644
--- a/pkg/util/util_test.go
+++ b/pkg/util/util_test.go
@@ -80,3 +80,39 @@ func Test_SplitStringOnLen(t *testing.T) {
 		})
 	}
 }
+
+func TestIntPointer(t *testing.T) {
+	tests := []struct {
+		name string
+		x    int
+		want int64
+	}{
+		{
+			name: "zero",
+			x:    0,
+			want: int64(0),
+		},
+		{
+			name: "positive",
+			x:    100,
+			want: int64(100),
+		},
+		{
+			name: "negative",
+			x:    -128,
+			want: int64(-128),
+		},
+		{
+			name: "int max",
+			x:    1<<31 - 1,
+			want: int64(1<<31 - 1),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := require.New(t)
+			got := IntPointer(tt.x)
+			req.Equal(tt.want, *got)
+		})
+	}
+}