hcornet 506716e703
Some checks failed
Deployment Verification / deploy-and-test (push) Failing after 29s
first sync
2025-03-04 07:59:21 +01:00

985 lines
28 KiB
Go

package main
// Docker
import (
"archive/tar"
"github.com/shuffle/shuffle-shared"
//"bufio"
"path/filepath"
//"strconv"
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
//"github.com/docker/docker"
"github.com/docker/docker/api/types"
//"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
newdockerclient "github.com/fsouza/go-dockerclient"
"github.com/go-git/go-billy/v5"
//network "github.com/docker/docker/api/types/network"
//natting "github.com/docker/go-connections/nat"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"time"
// "k8s.io/client-go/tools/clientcmd"
// "k8s.io/client-go/util/homedir"
)
// Parses a directory with a Dockerfile into a tar for Docker images..
func getParsedTar(tw *tar.Writer, baseDir, extra string) error {
return filepath.Walk(baseDir, func(file string, fi os.FileInfo, err error) error {
if file == baseDir {
return nil
}
//log.Printf("File: %s", file)
//log.Printf("Fileinfo: %#v", fi)
switch mode := fi.Mode(); {
case mode.IsDir():
// do directory recursion
//log.Printf("DIR: %s", file)
// Append "src" as extra here
filenamesplit := strings.Split(file, "/")
filename := fmt.Sprintf("%s%s/", extra, filenamesplit[len(filenamesplit)-1])
tmpExtra := fmt.Sprintf(filename)
//log.Printf("TmpExtra: %s", tmpExtra)
err = getParsedTar(tw, file, tmpExtra)
if err != nil {
log.Printf("Directory parse issue: %s", err)
return err
}
case mode.IsRegular():
// do file stuff
//log.Printf("FILE: %s", file)
fileReader, err := os.Open(file)
if err != nil {
return err
}
// Read the actual Dockerfile
readFile, err := ioutil.ReadAll(fileReader)
if err != nil {
log.Printf("Not file: %s", err)
return err
}
filenamesplit := strings.Split(file, "/")
filename := fmt.Sprintf("%s%s", extra, filenamesplit[len(filenamesplit)-1])
//log.Printf("Filename: %s", filename)
tarHeader := &tar.Header{
Name: filename,
Size: int64(len(readFile)),
}
//Writes the header described for the TAR file
err = tw.WriteHeader(tarHeader)
if err != nil {
return err
}
// Writes the dockerfile data to the TAR file
_, err = tw.Write(readFile)
if err != nil {
return err
}
}
return nil
})
}
// Custom TAR builder in memory for Docker images
func getParsedTarMemory(fs billy.Filesystem, tw *tar.Writer, baseDir, extra string) error {
// This one has to use baseDir + Extra
newBase := fmt.Sprintf("%s%s", baseDir, extra)
dir, err := fs.ReadDir(newBase)
if err != nil {
return err
}
for _, file := range dir {
// Folder?
switch mode := file.Mode(); {
case mode.IsDir():
filename := file.Name()
filenamesplit := strings.Split(filename, "/")
tmpExtra := fmt.Sprintf("%s%s/", extra, filenamesplit[len(filenamesplit)-1])
//log.Printf("EXTRA: %s", tmpExtra)
err = getParsedTarMemory(fs, tw, baseDir, tmpExtra)
if err != nil {
log.Printf("Directory parse issue: %s", err)
return err
}
case mode.IsRegular():
filenamesplit := strings.Split(file.Name(), "/")
filename := fmt.Sprintf("%s%s", extra, filenamesplit[len(filenamesplit)-1])
// Newbase
path := fmt.Sprintf("%s%s", newBase, file.Name())
fileReader, err := fs.Open(path)
if err != nil {
return err
}
//log.Printf("FILENAME: %s", filename)
readFile, err := ioutil.ReadAll(fileReader)
if err != nil {
log.Printf("Not file: %s", err)
return err
}
// Fixes issues with older versions of Docker and reference formats
// Specific to Shuffle rn. Could expand.
// FIXME: Seems like the issue was with multi-stage builds
/*
if filename == "Dockerfile" {
log.Printf("Should search and replace in readfile.")
referenceCheck := "FROM frikky/shuffle:"
if strings.Contains(string(readFile), referenceCheck) {
log.Printf("SHOULD SEARCH & REPLACE!")
newReference := fmt.Sprintf("FROM registry.hub.docker.com/frikky/shuffle:")
readFile = []byte(strings.Replace(string(readFile), referenceCheck, newReference, -1))
}
}
*/
//log.Printf("Filename: %s", filename)
// FIXME - might need the folder from EXTRA here
// Name has to be e.g. just "requirements.txt"
tarHeader := &tar.Header{
Name: filename,
Size: int64(len(readFile)),
}
//Writes the header described for the TAR file
err = tw.WriteHeader(tarHeader)
if err != nil {
return err
}
// Writes the dockerfile data to the TAR file
_, err = tw.Write(readFile)
if err != nil {
return err
}
}
}
return nil
}
/*
// Fixes App SDK issues.. meh
func fixTags(tags []string) []string {
checkTag := "frikky/shuffle"
newTags := []string{}
for _, tag := range tags {
if strings.HasPrefix(tag, checkTags) {
newTags.append(newTags, fmt.Sprintf("registry.hub.docker.com/%s", tag))
}
newTags.append(tag)
}
}
*/
// Custom Docker image builder wrapper in memory
func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder string, downloadIfFail bool) error {
ctx := context.Background()
client, err := client.NewEnvClient()
if err != nil {
log.Printf("Unable to create docker client: %s", err)
return err
}
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
defer tw.Close()
log.Printf("[INFO] Setting up memory build structure for folder: %s", dockerfileFolder)
err = getParsedTarMemory(fs, tw, dockerfileFolder, "")
if err != nil {
log.Printf("Tar issue: %s", err)
return err
}
dockerFileTarReader := bytes.NewReader(buf.Bytes())
// Dockerfile is inside the TAR itself. Not local context
// docker build --build-arg http_proxy=http://my.proxy.url
// Attempt at setting name according to #359: https://github.com/frikky/Shuffle/issues/359
labels := map[string]string{}
//target := ""
//if len(tags) > 0 {
// if strings.Contains(tags[0], ":") {
// version := strings.Split(tags[0], ":")
// if len(version) == 2 {
// target = fmt.Sprintf("shuffle-build-%s", version[1])
// tags = append(tags, target)
// labels["name"] = target
// }
// }
//}
buildOptions := types.ImageBuildOptions{
Remove: true,
Tags: tags,
BuildArgs: map[string]*string{},
Labels: labels,
}
// NetworkMode: "host",
httpProxy := os.Getenv("HTTP_PROXY")
if len(httpProxy) > 0 {
buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy
}
httpsProxy := os.Getenv("HTTPS_PROXY")
if len(httpProxy) > 0 {
buildOptions.BuildArgs["HTTPS_PROXY"] = &httpsProxy
}
// Build the actual image
log.Printf(`[INFO] Building %s with proxy "%s". Tags: "%s". This may take up to a few minutes.`, dockerfileFolder, httpsProxy, strings.Join(tags, ","))
imageBuildResponse, err := client.ImageBuild(
ctx,
dockerFileTarReader,
buildOptions,
)
//log.Printf("RESPONSE: %#v", imageBuildResponse)
//log.Printf("Response: %#v", imageBuildResponse.Body)
//log.Printf("[DEBUG] IMAGERESPONSE: %#v", imageBuildResponse.Body)
if imageBuildResponse.Body != nil {
defer imageBuildResponse.Body.Close()
buildBuf := new(strings.Builder)
_, newerr := io.Copy(buildBuf, imageBuildResponse.Body)
if newerr != nil {
log.Printf("[WARNING] Failed reading Docker build STDOUT: %s", newerr)
} else {
log.Printf("[INFO] STRING: %s", buildBuf.String())
if strings.Contains(buildBuf.String(), "errorDetail") {
log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n"))
// Handles pulling of the same image if applicable
// This fixes some issues with older versions of Docker which can't build
// on their own ( <17.05 )
pullOptions := types.ImagePullOptions{}
downloaded := false
for _, image := range tags {
// Is this ok? Not sure. Tags shouldn't be controlled here prolly.
image = strings.ToLower(image)
newImage := fmt.Sprintf("%s/%s", registryName, image)
log.Printf("[INFO] Pulling image %s", newImage)
reader, err := client.ImagePull(ctx, newImage, pullOptions)
if err != nil {
log.Printf("[ERROR] Failed getting image %s: %s", newImage, err)
continue
}
// Attempt to retag the image to not contain registry...
//newBuf := buildBuf
downloaded = true
io.Copy(os.Stdout, reader)
log.Printf("[INFO] Successfully downloaded and built %s", newImage)
}
if !downloaded {
return errors.New(fmt.Sprintf("Failed to build / download images %s", strings.Join(tags, ",")))
}
//baseDockerName
}
}
}
if err != nil {
// Read the STDOUT from the build process
return err
}
return nil
}
func getK8sClient() (*kubernetes.Clientset, error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, fmt.Errorf("[ERROR] failed to get in-cluster config: %v", err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("[ERROR] failed to create Kubernetes client: %v", err)
}
return clientset, nil
}
func deleteJob(client *kubernetes.Clientset, jobName, namespace string) error {
deletePolicy := metav1.DeletePropagationForeground
return client.BatchV1().Jobs(namespace).Delete(context.TODO(), jobName, metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
})
}
func buildImage(tags []string, dockerfileFolder string) error {
isKubernetes := false
if os.Getenv("IS_KUBERNETES") == "true" {
isKubernetes = true
}
if isKubernetes {
// log.Printf("K8S ###################")
// log.Print("dockerfileFolder: ", dockerfileFolder)
// log.Print("tags: ", tags)
// log.Print("only tag: ", tags[1])
registryName := ""
if len(os.Getenv("REGISTRY_URL")) > 0 {
registryName = os.Getenv("REGISTRY_URL")
}
log.Printf("[INFO] registry name: %s", registryName)
contextDir := strings.Replace(dockerfileFolder, "Dockerfile", "", -1)
contextDir = "/app/" + contextDir
log.Print("contextDir: ", contextDir)
dockerFile := "./Dockerfile"
client, err := getK8sClient()
if err != nil {
fmt.Printf("Unable to authencticate : %v\n", err)
return err
}
BackendPodLabel := "io.kompose.service=backend"
backendPodList, podListErr := client.CoreV1().Pods("shuffle").List(context.TODO(), metav1.ListOptions{
LabelSelector: BackendPodLabel,
})
if podListErr != nil || len(backendPodList.Items) == 0 {
fmt.Println("Error getting backend pod or no pod found:", podListErr)
return podListErr
}
backendNodeName := backendPodList.Items[0].Spec.NodeName
log.Printf("[INFO] Backend running on: %s", backendNodeName)
job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "shuffle-app-builder",
},
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "kaniko",
Image: "gcr.io/kaniko-project/executor:latest",
Args: []string{
"--verbosity=debug",
"--dockerfile=" + dockerFile,
"--context=dir://" + contextDir,
"--skip-tls-verify",
"--destination=" + registryName + "/" + tags[1],
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "kaniko-workspace",
MountPath: "/app/generated",
},
},
},
},
NodeSelector: map[string]string{
"node": backendNodeName,
},
RestartPolicy: corev1.RestartPolicyNever,
Volumes: []corev1.Volume{
{
Name: "kaniko-workspace",
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: "backend-apps-claim",
},
},
},
},
},
},
},
}
createdJob, err := client.BatchV1().Jobs("shuffle").Create(context.TODO(), job, metav1.CreateOptions{})
if err != nil {
log.Printf("Failed to start image builder job: %s", err)
return err
}
timeout := time.After(5 * time.Minute)
tick := time.Tick(5 * time.Second)
for {
select {
case <-timeout:
return fmt.Errorf("job didn't complete within the expected time")
case <-tick:
currentJob, err := client.BatchV1().Jobs("shuffle").Get(context.TODO(), createdJob.Name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("[ERROR] failed to fetch %s status: %v", createdJob.Name, err)
}
if currentJob.Status.Succeeded > 0 {
log.Printf("[INFO] Job %s completed successfully!", createdJob.Name)
log.Printf("[INFO] Cleaning up the job %s", createdJob.Name)
err := deleteJob(client, createdJob.Name, "shuffle")
if err != nil {
return fmt.Errorf("[ERROR] failed deleting job %s with error: %s", createdJob.Name, err)
}
log.Println("Job deleted successfully!")
return nil
} else if currentJob.Status.Failed > 0 {
log.Printf("[ERROR] %s job failed with error: %s", createdJob.Name, err)
err := deleteJob(client, createdJob.Name, "shuffle")
if err != nil {
return fmt.Errorf("[ERROR] failed deleting job %s with error: %s", createdJob.Name, err)
}
}
}
}
} else {
ctx := context.Background()
client, err := client.NewEnvClient()
if err != nil {
log.Printf("Unable to create docker client: %s", err)
return err
}
log.Printf("[INFO] Docker Tags: %s", tags)
dockerfileSplit := strings.Split(dockerfileFolder, "/")
// Create a buffer
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
defer tw.Close()
baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/")
// Builds the entire folder into buf
err = getParsedTar(tw, baseDir, "")
if err != nil {
log.Printf("Tar issue: %s", err)
}
dockerFileTarReader := bytes.NewReader(buf.Bytes())
buildOptions := types.ImageBuildOptions{
Remove: true,
Tags: tags,
BuildArgs: map[string]*string{},
}
//NetworkMode: "host",
httpProxy := os.Getenv("HTTP_PROXY")
if len(httpProxy) > 0 {
buildOptions.BuildArgs["HTTP_PROXY"] = &httpProxy
}
httpsProxy := os.Getenv("HTTPS_PROXY")
if len(httpProxy) > 0 {
buildOptions.BuildArgs["https_proxy"] = &httpsProxy
}
// Build the actual image
imageBuildResponse, err := client.ImageBuild(
ctx,
dockerFileTarReader,
buildOptions,
)
if err != nil {
return err
}
// Read the STDOUT from the build process
defer imageBuildResponse.Body.Close()
buildBuf := new(strings.Builder)
_, err = io.Copy(buildBuf, imageBuildResponse.Body)
if err != nil {
return err
} else {
if strings.Contains(buildBuf.String(), "errorDetail") {
log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n"))
return errors.New(fmt.Sprintf("Failed building %s. Check backend logs for details. Most likely means you have an old version of Docker.", strings.Join(tags, ",")))
}
}
}
return nil
}
// Checks if an image exists
func imageCheckBuilder(images []string) error {
//log.Printf("[FIXME] ImageNames to check: %#v", images)
return nil
ctx := context.Background()
client, err := client.NewEnvClient()
if err != nil {
log.Printf("Unable to create docker client: %s", err)
return err
}
allImages, err := client.ImageList(ctx, types.ImageListOptions{
All: true,
})
if err != nil {
log.Printf("[ERROR] Failed creating imagelist: %s", err)
return err
}
filteredImages := []types.ImageSummary{}
for _, image := range allImages {
found := false
for _, repoTag := range image.RepoTags {
if strings.Contains(repoTag, baseDockerName) {
found = true
break
}
}
if found {
filteredImages = append(filteredImages, image)
}
}
// FIXME: Continue fixing apps here
// https://github.com/frikky/Shuffle/issues/135
// 1. Find if app exists
// 2. Create app if it doesn't
//log.Printf("Apps: %#v", filteredImages)
return nil
}
// https://stackoverflow.com/questions/23935141/how-to-copy-docker-images-from-one-host-to-another-without-using-a-repository
func getDockerImage(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request)
if cors {
return
}
// Just here to verify that the user is logged in
//_, err := shuffle.HandleApiAuthentication(resp, request)
//if err != nil {
// log.Printf("[WARNING] Api authentication failed in DOWNLOAD IMAGE: %s", err)
// resp.WriteHeader(401)
// resp.Write([]byte(`{"success": false}`))
// return
//}
body, err := ioutil.ReadAll(request.Body)
if err != nil {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Failed reading body"}`))
return
}
// This has to be done in a weird way because Datastore doesn't
// support map[string]interface and similar (openapi3.Swagger)
var version shuffle.DockerRequestCheck
err = json.Unmarshal(body, &version)
if err != nil {
resp.WriteHeader(422)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed JSON marshalling: %s"}`, err)))
return
}
//log.Printf("[DEBUG] Image to load: %s", version.Name)
dockercli, err := client.NewEnvClient()
if err != nil {
log.Printf("[WARNING] Unable to create docker client: %s", err)
resp.WriteHeader(422)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed JSON marshalling: %s"}`, err)))
return
}
ctx := context.Background()
images, err := dockercli.ImageList(ctx, types.ImageListOptions{
All: true,
})
img := types.ImageSummary{}
tagFound := ""
img2 := types.ImageSummary{}
tagFound2 := ""
alternativeNameSplit := strings.Split(version.Name, "/")
alternativeName := version.Name
if len(alternativeNameSplit) == 3 {
alternativeName = strings.Join(alternativeNameSplit[1:3], "/")
}
log.Printf("[INFO] Trying to download image: %s. Alt: %s", version.Name, alternativeName)
for _, image := range images {
for _, tag := range image.RepoTags {
//log.Printf("[DEBUG] Tag: %s", tag)
if strings.ToLower(tag) == strings.ToLower(version.Name) {
img = image
tagFound = tag
break
}
if strings.ToLower(tag) == strings.ToLower(alternativeName) {
img2 = image
tagFound2 = tag
}
}
}
pullOptions := types.ImagePullOptions{}
if len(img.ID) == 0 {
_, err := dockercli.ImagePull(context.Background(), version.Name, pullOptions)
if err == nil {
tagFound = version.Name
img.ID = version.Name
img2.ID = version.Name
dockercli.ImageTag(ctx, version.Name, alternativeName)
}
}
if len(img2.ID) == 0 {
_, err := dockercli.ImagePull(context.Background(), alternativeName, pullOptions)
if err == nil {
tagFound = alternativeName
img.ID = alternativeName
img2.ID = alternativeName
dockercli.ImageTag(ctx, alternativeName, version.Name)
}
}
// REBUILDS THE APP
if len(img.ID) == 0 {
if len(img2.ID) == 0 {
workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 0, 0)
log.Printf("[INFO] Getting workflowapps for a rebuild. Got %d with err %#v", len(workflowapps), err)
if err == nil {
imageName := ""
imageVersion := ""
newNameSplit := strings.Split(version.Name, ":")
if len(newNameSplit) == 2 {
//log.Printf("[DEBUG] Found name %#v", newNameSplit)
findVersionSplit := strings.Split(newNameSplit[1], "_")
//log.Printf("[DEBUG] Found another split %#v", findVersionSplit)
if len(findVersionSplit) == 2 {
imageVersion = findVersionSplit[len(findVersionSplit)-1]
imageName = findVersionSplit[0]
} else if len(findVersionSplit) >= 2 {
imageVersion = findVersionSplit[len(findVersionSplit)-1]
imageName = strings.Join(findVersionSplit[0:len(findVersionSplit)-1], "_")
} else {
log.Printf("[DEBUG] Couldn't parse appname & version for %#v", findVersionSplit)
}
}
if len(imageName) > 0 && len(imageVersion) > 0 {
foundApp := shuffle.WorkflowApp{}
imageName = strings.ToLower(imageName)
imageVersion = strings.ToLower(imageVersion)
log.Printf("[DEBUG] Docker Looking for appname %s with version %s", imageName, imageVersion)
for _, app := range workflowapps {
if strings.ToLower(strings.Replace(app.Name, " ", "_", -1)) == imageName && app.AppVersion == imageVersion {
if app.Generated {
log.Printf("[DEBUG] Found matching app %s:%s - %s", imageName, imageVersion, app.ID)
foundApp = app
break
} else {
log.Printf("[WARNING] Trying to rebuild app that isn't generated - not allowed. Looking further.")
}
//break
}
}
if len(foundApp.ID) > 0 {
openApiApp, err := shuffle.GetOpenApiDatastore(ctx, foundApp.ID)
if err != nil {
log.Printf("[ERROR] Failed getting OpenAPI app %s to database: %s", foundApp.ID, err)
} else {
log.Printf("[DEBUG] Found OpenAPI app for %s as generated - now building!", version.Name)
user := shuffle.User{}
//img = version.Name
if len(alternativeName) > 0 {
tagFound = alternativeName
} else {
tagFound = version.Name
}
buildSwaggerApp(resp, []byte(openApiApp.Body), user, false)
}
}
}
} else {
log.Printf("[WARNING] Couldn't find an image with registry name %s and %s", version.Name, alternativeName)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't find image %s"}`, version.Name)))
return
}
}
if len(tagFound) == 0 && len(tagFound2) > 0 {
img = img2
tagFound = tagFound2
}
}
//log.Printf("[INFO] Img found (%s): %#v", tagFound, img)
//log.Printf("[INFO] Img found to be downloaded by client: %s", tagFound)
newClient, err := newdockerclient.NewClientFromEnv()
if err != nil {
log.Printf("[ERROR] Failed setting up docker env: %#v", newClient)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't make docker client"}`)))
return
}
////https://github.com/fsouza/go-dockerclient/issues/600
//defer fileReader.Close()
opts := newdockerclient.ExportImageOptions{
Name: tagFound,
OutputStream: resp,
}
if err := newClient.ExportImage(opts); err != nil {
log.Printf("[ERROR] FAILED to save image to file: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't export image"}`)))
return
}
//resp.WriteHeader(200)
}
// Downloads and activates an app from shuffler.io if possible
func handleRemoteDownloadApp(resp http.ResponseWriter, ctx context.Context, user shuffle.User, appId string) {
url := fmt.Sprintf("https://shuffler.io/api/v1/apps/%s/config", appId)
log.Printf("Downloading API from %s", url)
req, err := http.NewRequest(
"GET",
url,
nil,
)
if err != nil {
log.Printf("[ERROR] Failed auto-downloading app %s: %s", appId, err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
httpClient := &http.Client{}
newresp, err := httpClient.Do(req)
if err != nil {
log.Printf("[ERROR] Failed running auto-download request for %s: %s", appId, err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
respBody, err := ioutil.ReadAll(newresp.Body)
if err != nil {
log.Printf("[ERROR] Failed setting respbody for workflow download: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
if len(respBody) > 0 {
type tmpapp struct {
Success bool `json:"success"`
OpenAPI string `json:"openapi"`
}
app := tmpapp{}
err := json.Unmarshal(respBody, &app)
if err != nil || app.Success == false || len(app.OpenAPI) == 0 {
log.Printf("[ERROR] Failed app unmarshal during auto-download. Success: %#v. Applength: %d: %s", app.Success, len(app.OpenAPI), err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
key, err := base64.StdEncoding.DecodeString(app.OpenAPI)
if err != nil {
log.Printf("[ERROR] Failed auto-setting OpenAPI app: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
cacheKey := fmt.Sprintf("workflowapps-sorted-100")
shuffle.DeleteCache(ctx, cacheKey)
cacheKey = fmt.Sprintf("workflowapps-sorted-500")
shuffle.DeleteCache(ctx, cacheKey)
cacheKey = fmt.Sprintf("workflowapps-sorted-1000")
shuffle.DeleteCache(ctx, cacheKey)
newapp := shuffle.ParsedOpenApi{}
err = json.Unmarshal(key, &newapp)
if err != nil {
log.Printf("[ERROR] Failed openapi unmarshal during auto-download: %s", app.Success, len(app.OpenAPI), err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
err = json.Unmarshal(key, &newapp)
if err != nil {
log.Printf("[ERROR] Failed openapi unmarshal during auto-download: %s", app.Success, len(app.OpenAPI), err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
return
}
buildSwaggerApp(resp, []byte(newapp.Body), user, true)
return
}
}
func activateWorkflowAppDocker(resp http.ResponseWriter, request *http.Request) {
cors := shuffle.HandleCors(resp, request)
if cors {
return
}
user, err := shuffle.HandleApiAuthentication(resp, request)
if err != nil {
log.Printf("[WARNING] Api authentication failed in get active apps: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
if user.Role == "org-reader" {
log.Printf("[WARNING] Org-reader doesn't have access to activate workflow app (shared): %s (%s)", user.Username, user.Id)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Read only user"}`))
return
}
ctx := context.Background()
location := strings.Split(request.URL.String(), "/")
var fileId string
if location[1] == "api" {
if len(location) <= 4 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
fileId = location[4]
}
app, err := shuffle.GetApp(ctx, fileId, user, false)
if err != nil {
appName := request.URL.Query().Get("app_name")
appVersion := request.URL.Query().Get("app_version")
if len(appName) > 0 && len(appVersion) > 0 {
apps, err := shuffle.FindWorkflowAppByName(ctx, appName)
//log.Printf("[INFO] Found %d apps for %s", len(apps), appName)
if err != nil || len(apps) == 0 {
log.Printf("[WARNING] Error getting app %s (app config). Starting remote download.: %s", appName, err)
handleRemoteDownloadApp(resp, ctx, user, fileId)
return
}
selectedApp := shuffle.WorkflowApp{}
for _, app := range apps {
if !app.Sharing && !app.Public {
continue
}
if app.Name == appName {
selectedApp = app
}
if app.Name == appName && app.AppVersion == appVersion {
selectedApp = app
}
}
app = &selectedApp
} else {
log.Printf("[WARNING] Error getting app with ID %s (app config): %s. Starting remote download(2)", fileId, err)
handleRemoteDownloadApp(resp, ctx, user, fileId)
return
//resp.WriteHeader(401)
//resp.Write([]byte(`{"success": false, "reason": "App doesn't exist"}`))
//return
}
}
// Just making sure it's being built properly
if app == nil {
log.Printf("[WARNING] App is nil. This shouldn't happen. Starting remote download(3)")
handleRemoteDownloadApp(resp, ctx, user, fileId)
return
}
// Check the app.. hmm
openApiApp, err := shuffle.GetOpenApiDatastore(ctx, app.ID)
if err != nil {
log.Printf("[WARNING] Error getting app %s (openapi config): %s", app.ID, err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Couldn't find app OpenAPI"}`))
return
}
log.Printf("[INFO] User %s (%s) is activating %s. Public: %t, Shared: %t", user.Username, user.Id, app.Name, app.Public, app.Sharing)
buildSwaggerApp(resp, []byte(openApiApp.Body), user, true)
//app.Active = true
//app.Generated = true
//app, err := shuffle.SetApp(ctx, app)
//resp.WriteHeader(200)
//resp.Write([]byte(`{"success": true}`))
}