From 3829a9925fa066e6559b9daad8c147229590cd82 Mon Sep 17 00:00:00 2001 From: PatrickMenoti <82882574+PatrickMenoti@users.noreply.github.com> Date: Mon, 2 Sep 2024 09:47:11 -0300 Subject: [PATCH] feat: add initial equalized deploy command --- pkg/api/storage/credentials.go | 26 ++++++ pkg/cmd/deploy/deploy.go | 160 ++++++++----------------------- pkg/cmd/deploy/purge.go | 166 --------------------------------- pkg/cmd/deploy/requests.go | 77 ++------------- pkg/cmd/deploy/scriptrunner.go | 91 ++++++++++++++++++ pkg/cmd/deploy/upload.go | 15 ++- pkg/token/models.go | 3 + 7 files changed, 177 insertions(+), 361 deletions(-) create mode 100644 pkg/api/storage/credentials.go delete mode 100644 pkg/cmd/deploy/purge.go create mode 100644 pkg/cmd/deploy/scriptrunner.go diff --git a/pkg/api/storage/credentials.go b/pkg/api/storage/credentials.go new file mode 100644 index 000000000..f564f17b7 --- /dev/null +++ b/pkg/api/storage/credentials.go @@ -0,0 +1,26 @@ +package storage + +import ( + "context" + + "github.com/aziontech/azion-cli/pkg/logger" + "github.com/aziontech/azion-cli/utils" + sdk "github.com/aziontech/azionapi-go-sdk/storage" + "go.uber.org/zap" +) + +type RequestCredentials struct { + sdk.S3CredentialCreate +} + +func (c *Client) CreateCredentials(ctx context.Context, request RequestCredentials) (*sdk.ResponseS3Credential, error) { + logger.Debug("Creating s3 credentials ", zap.Any("name", request.Name)) + req := c.apiClient.StorageAPI.StorageApiS3CredentialsCreate(ctx).S3CredentialCreate(request.S3CredentialCreate) + // req := c.apiClient.StorageAPI.StorageApiBucketsCreate(ctx).BucketCreate(request.BucketCreate) + resp, httpResp, err := req.Execute() + if err != nil { + logger.Debug("Error while creating the user's s3 credentials", zap.Error(err)) + return nil, utils.ErrorPerStatusCode(httpResp, err) + } + return resp, nil +} diff --git a/pkg/cmd/deploy/deploy.go b/pkg/cmd/deploy/deploy.go index f395f945f..8656eadc5 100644 --- a/pkg/cmd/deploy/deploy.go +++ b/pkg/cmd/deploy/deploy.go @@ -7,21 +7,20 @@ import ( "io/fs" "os" "path/filepath" - "strconv" + "time" "github.com/MakeNowJust/heredoc" msg "github.com/aziontech/azion-cli/messages/deploy" - apiEdgeApplications "github.com/aziontech/azion-cli/pkg/api/edge_applications" + "github.com/aziontech/azion-cli/pkg/api/storage" "github.com/aziontech/azion-cli/pkg/cmd/build" - "github.com/aziontech/azion-cli/pkg/cmd/sync" "github.com/aziontech/azion-cli/pkg/cmdutil" "github.com/aziontech/azion-cli/pkg/contracts" "github.com/aziontech/azion-cli/pkg/iostreams" "github.com/aziontech/azion-cli/pkg/logger" manifestInt "github.com/aziontech/azion-cli/pkg/manifest" - "github.com/aziontech/azion-cli/pkg/output" + "github.com/aziontech/azion-cli/pkg/token" "github.com/aziontech/azion-cli/utils" - sdk "github.com/aziontech/azionapi-go-sdk/edgeapplications" + sdk "github.com/aziontech/azionapi-go-sdk/storage" "github.com/spf13/cobra" "go.uber.org/zap" ) @@ -114,157 +113,78 @@ func (cmd *DeployCmd) Run(f *cmdutil.Factory) error { msgs = append(msgs, "Running deploy command") ctx := context.Background() - err := checkToken(f) + settings, err := token.ReadSettings() if err != nil { return err } - if Sync { - sync.ProjectConf = ProjectConf - syncCmd := sync.NewSyncCmd(f) - syncCmd.EnvPath = Env - if err := sync.Run(syncCmd); err != nil { - logger.Debug("Error while synchronizing local resources with remove resources", zap.Error(err)) - return err - } - } - - if !SkipBuild { - buildCmd := cmd.BuildCmd(f) - err = buildCmd.ExternalRun(&contracts.BuildInfo{}, ProjectConf, &msgs) - if err != nil { - logger.Debug("Error while running build command called by deploy command", zap.Error(err)) - return err - } - } - conf, err := cmd.GetAzionJsonContent(ProjectConf) if err != nil { logger.Debug("Failed to get Azion JSON content", zap.Error(err)) return err } - versionID := cmd.VersionID() - - conf.Prefix = versionID - - err = checkArgsJson(cmd, ProjectConf) - if err != nil { - return err - } - - clients := NewClients(f) - interpreter := cmd.Interpreter() - - pathManifest, err := interpreter.ManifestPath() - if err != nil { - return err - } - - err = cmd.doApplication(clients.EdgeApplication, context.Background(), conf, &msgs) - if err != nil { - return err - } - - singleOriginId, err := cmd.doOriginSingle(clients.Origin, ctx, conf, &msgs) - if err != nil { - return err - } - - err = cmd.doBucket(clients.Bucket, ctx, conf, &msgs) - if err != nil { - return err - } - - // Check if directory exists; if not, we skip uploading static files - if _, err := os.Stat(PathStatic); os.IsNotExist(err) { - logger.Debug(msg.SkipUpload) - } else { - err = cmd.uploadFiles(f, conf, &msgs) + //create credentials if they are not found on settings file + if settings.S3AccessKey == "" || settings.S3SecreKey == "" { + nameBucket := fmt.Sprintf("%s-%s", conf.Name, cmd.VersionID()) + storageClient := storage.NewClient(f.HttpClient, f.Config.GetString("storage_url"), f.Config.GetString("token")) + err := storageClient.CreateBucket(ctx, storage.RequestBucket{BucketCreate: sdk.BucketCreate{Name: nameBucket, EdgeAccess: sdk.READ_WRITE}}) if err != nil { return err } - } - conf.Function.File = ".edge/worker.js" - err = cmd.doFunction(clients, ctx, conf, &msgs) - if err != nil { - return err - } + // Get the current time + now := time.Now() - if !conf.NotFirstRun { - ruleDefaultID, err := clients.EdgeApplication.GetRulesDefault(ctx, conf.Application.ID, "request") - if err != nil { - logger.Debug("Error while getting default rules engine", zap.Error(err)) - return err - } - behaviors := make([]sdk.RulesEngineBehaviorEntry, 0) - - var behString sdk.RulesEngineBehaviorString - behString.SetName("set_origin") + // Add one year to the current time + oneYearLater := now.AddDate(1, 0, 0) - behString.SetTarget(strconv.Itoa(int(singleOriginId))) + request := new(storage.RequestCredentials) + request.Name = &nameBucket + request.Capabilities = []string{"listAllBucketNames", "listBuckets", "listFiles", "readFiles", "writeFiles", "deleteFiles"} + request.Bucket = &nameBucket + request.ExpirationDate = &oneYearLater - behaviors = append(behaviors, sdk.RulesEngineBehaviorEntry{ - RulesEngineBehaviorString: &behString, - }) - - reqUpdateRulesEngine := apiEdgeApplications.UpdateRulesEngineRequest{ - IdApplication: conf.Application.ID, - Phase: "request", - Id: ruleDefaultID, + creds, err := storageClient.CreateCredentials(ctx, *request) + if err != nil { + return err } + settings.S3AccessKey = creds.Data.GetAccessKey() + settings.S3SecreKey = creds.Data.GetSecretKey() + settings.S3Bucket = nameBucket - reqUpdateRulesEngine.SetBehaviors(behaviors) - - _, err = clients.EdgeApplication.UpdateRulesEngine(ctx, &reqUpdateRulesEngine) + err = token.WriteSettings(settings) if err != nil { - logger.Debug("Error while updating default rules engine", zap.Error(err)) return err } } - manifestStructure, err := interpreter.ReadManifest(pathManifest, f, &msgs) + localDir, err := cmd.GetWorkDir() if err != nil { return err } - if len(conf.RulesEngine.Rules) == 0 { - err = cmd.doRulesDeploy(ctx, conf, clients.EdgeApplication, &msgs) - if err != nil { - return err - } - } + conf.Prefix = cmd.VersionID() - err = interpreter.CreateResources(conf, manifestStructure, f, ProjectConf, &msgs) + err = cmd.uploadFiles(f, conf, &msgs, localDir) if err != nil { return err } - if manifestStructure.Domain.Name == "" { - err = cmd.doDomain(clients.Domain, ctx, conf, &msgs) - if err != nil { - return err - } + id, err := callScript("azion3c6bad99a7f30a2491e5423e227050aa72a", settings.S3AccessKey, settings.S3SecreKey, conf.Prefix, settings.S3Bucket) + if err != nil { + return err } + fmt.Println(id) - logger.FInfoFlags(cmd.F.IOStreams.Out, msg.DeploySuccessful, f.Format, f.Out) - msgs = append(msgs, msg.DeploySuccessful) - - msgfOutputDomainSuccess := fmt.Sprintf(msg.DeployOutputDomainSuccess, conf.Domain.Url) - logger.FInfoFlags(cmd.F.IOStreams.Out, msgfOutputDomainSuccess, f.Format, f.Out) - msgs = append(msgs, msgfOutputDomainSuccess) + fmt.Println("5") - logger.FInfoFlags(cmd.F.IOStreams.Out, msg.DeployPropagation, f.Format, f.Out) - msgs = append(msgs, msg.DeployPropagation) - - outSlice := output.SliceOutput{ - Messages: msgs, - GeneralOutput: output.GeneralOutput{ - Out: cmd.F.IOStreams.Out, - Flags: cmd.F.Flags, - }, + err = openBrowser(f, fmt.Sprintf("https://stage-console.azion.com/create/deploy/%s", id)) + if err != nil { + return err } - return output.Print(&outSlice) + fmt.Println("6") + + return nil } diff --git a/pkg/cmd/deploy/purge.go b/pkg/cmd/deploy/purge.go deleted file mode 100644 index 300f43732..000000000 --- a/pkg/cmd/deploy/purge.go +++ /dev/null @@ -1,166 +0,0 @@ -package deploy - -import ( - "context" - "crypto/sha256" - "encoding/json" - "fmt" - "os" - "path" - "path/filepath" - "strings" - - msg "github.com/aziontech/azion-cli/messages/deploy" - apidom "github.com/aziontech/azion-cli/pkg/api/domain" - apipurge "github.com/aziontech/azion-cli/pkg/api/realtime_purge" - "github.com/aziontech/azion-cli/pkg/logger" - "go.uber.org/zap" -) - -type Data struct { - Name string `json:"name"` - Hash string `json:"hash"` -} - -func (cmd *DeployCmd) PurgeWildcard(domain []string, path string) error { - purgeDomains := make([]string, len(domain)) - for i := 0; i < len(domain); i++ { - purgeDomains[i] = domain[i] + path - } - ctx := context.Background() - clipurge := apipurge.NewClient(cmd.F.HttpClient, cmd.F.Config.GetString("api_url"), cmd.F.Config.GetString("token")) - err := clipurge.PurgeWildcard(ctx, purgeDomains) - if err != nil { - logger.Debug("Error while purging wildcard domain", zap.Error(err)) - return err - } - return nil -} - -func (cmd *DeployCmd) PurgeUrls(domain []string, path string) error { - purgeDomains := make([]string, len(domain)) - for i := 0; i < len(domain); i++ { - purgeDomains[i] = domain[i] + path - } - ctx := context.Background() - clipurge := apipurge.NewClient(cmd.F.HttpClient, cmd.F.Config.GetString("api_url"), cmd.F.Config.GetString("token")) - err := clipurge.PurgeUrls(ctx, purgeDomains) - if err != nil { - logger.Debug("Error while purging urls domain", zap.Error(err)) - return err - } - return nil -} - -func PurgeForUpdatedFiles(cmd *DeployCmd, domain apidom.DomainResponse, confPath string, msgs *[]string) error { - if _, err := os.Stat(PathStatic); os.IsNotExist(err) { - return nil - } - listURLsDomains := domain.GetCnames() - if !domain.GetCnameAccessOnly() { - listURLsDomains = append(listURLsDomains, domain.GetDomainName()) - } - - currentDataMap, err := ReadFilesJSONL() - if err != nil { - return err - } - - if currentDataMap == nil { - wildCard := "/*" - for _, v := range listURLsDomains { - if err := cmd.PurgeWildcard([]string{v}, wildCard); err != nil { - logger.Debug("Error purge path domain", zap.String("wildCard", wildCard), zap.Error(err)) - } - msgsf := fmt.Sprintf(msg.DeployOutputCachePurgeWildCard, v) - logger.FInfoFlags(cmd.F.IOStreams.Out, msgsf, cmd.F.Format, cmd.F.Out) - *msgs = append(*msgs, msgsf) - } - } - - newData, err := ReadFilesEdgeStorage() - if err != nil { - return err - } - - if currentDataMap != nil { - newDataMap := make(map[string]Data) - for _, newDataItem := range newData { - newDataMap[newDataItem.Name] = newDataItem - } - - for _, current := range currentDataMap { - if newDataItem, exists := newDataMap[current.Name]; exists { - if current.Hash != newDataItem.Hash { - path := strings.TrimPrefix(current.Name, ".edge/storage") - if err := cmd.PurgeUrls(listURLsDomains, path); err != nil { - logger.Debug("Error purge path domain", zap.String("path", path), zap.Error(err)) - } - logger.FInfo(cmd.F.IOStreams.Out, fmt.Sprintf(msg.DeployOutputCachePurgeUrl, current.Name)) - } - } - } - } - - jsonl, err := json.MarshalIndent(newData, " ", " ") - if err != nil { - return err - } - - jsonlFile, err := os.Create(path.Join(confPath, "files.json")) - if err != nil { - return err - } - defer jsonlFile.Close() - - if _, err := jsonlFile.Write(jsonl); err != nil { - return err - } - - return nil -} - -func ReadFilesJSONL() ([]Data, error) { - var dt []Data - file, err := os.Open("./azion/files.json") - if os.IsNotExist(err) { - return dt, nil - } - if err != nil { - return nil, err - } - defer file.Close() - - decoder := json.NewDecoder(file) - if err := decoder.Decode(&dt); err != nil { - return nil, err - } - return dt, nil -} - -func ReadFilesEdgeStorage() ([]Data, error) { - var data []Data - err := filepath.Walk(".edge/storage", func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - content, err := os.ReadFile(path) - if err != nil { - logger.Debug("Error read file", zap.Error(err)) - return err - } - dt := Data{ - Name: path, - Hash: fmt.Sprintf("%x", sha256.Sum256(content)), - } - data = append(data, dt) - } - return nil - }) - if err != nil { - logger.Debug("Error filepath walk directory", zap.Error(err)) - return nil, err - } - return data, nil -} diff --git a/pkg/cmd/deploy/requests.go b/pkg/cmd/deploy/requests.go index 755688292..7c303ba73 100644 --- a/pkg/cmd/deploy/requests.go +++ b/pkg/cmd/deploy/requests.go @@ -10,6 +10,7 @@ import ( sdk "github.com/aziontech/azionapi-go-sdk/edgeapplications" thoth "github.com/aziontech/go-thoth" + "github.com/skratchdot/open-golang/open" "go.uber.org/zap" msg "github.com/aziontech/azion-cli/messages/deploy" @@ -160,73 +161,6 @@ func (cmd *DeployCmd) doApplication( return nil } -func (cmd *DeployCmd) doDomain(client *apidom.Client, ctx context.Context, conf *contracts.AzionApplicationOptions, msgs *[]string) error { - var domain apidom.DomainResponse - var err error - - newDomain := false - if conf.Domain.Id == 0 { - var projName string - for { - domain, err = cmd.createDomain(client, ctx, conf, msgs) - if err != nil { - // if the name is already in use, we ask for another one - if strings.Contains(err.Error(), utils.ErrorNameInUse.Error()) { - if NoPrompt { - return err - } - logger.FInfoFlags(cmd.Io.Out, msg.DomainInUse, cmd.F.Format, cmd.F.Out) - *msgs = append(*msgs, msg.DomainInUse) - if Auto { - projName = fmt.Sprintf("%s-%s", conf.Name, utils.Timestamp()) - msgf := fmt.Sprintf(msg.NameInUseApplication, projName) - logger.FInfoFlags(cmd.Io.Out, msgf, cmd.F.Format, cmd.F.Out) - *msgs = append(*msgs, msgf) - projName = thoth.GenerateName() - } else { - projName, err = askForInput(msg.AskInputName, thoth.GenerateName()) - if err != nil { - return err - } - } - conf.Domain.Name = projName - continue - } - return err - } - conf.Domain.Id = domain.GetId() - conf.Domain.Name = domain.GetName() - conf.Domain.DomainName = domain.GetDomainName() - conf.Domain.Url = utils.Concat("https://", domain.GetDomainName()) - newDomain = true - break - } - - err = cmd.WriteAzionJsonContent(conf, ProjectConf) - if err != nil { - logger.Debug("Error while writing azion.json file", zap.Error(err)) - return err - } - - } else { - domain, err = cmd.updateDomain(client, ctx, conf, msgs) - if err != nil { - logger.Debug("Error while updating domain", zap.Error(err)) - return err - } - } - - if conf.RtPurge.PurgeOnPublish && !newDomain { - err = PurgeForUpdatedFiles(cmd, domain, ProjectConf, msgs) - if err != nil { - logger.Debug("Error while purging domain", zap.Error(err)) - return err - } - } - - return nil -} - func (cmd *DeployCmd) doRulesDeploy( ctx context.Context, conf *contracts.AzionApplicationOptions, @@ -610,3 +544,12 @@ func checkArgsJson(cmd *DeployCmd, projectPath string) error { return nil } + +func openBrowser(f *cmdutil.Factory, urlSsoNext string) error { + logger.FInfo(f.IOStreams.Out, "msg.VisitMsg") + err := open.Run(urlSsoNext) + if err != nil { + return err + } + return nil +} diff --git a/pkg/cmd/deploy/scriptrunner.go b/pkg/cmd/deploy/scriptrunner.go new file mode 100644 index 000000000..bf324fb26 --- /dev/null +++ b/pkg/cmd/deploy/scriptrunner.go @@ -0,0 +1,91 @@ +package deploy + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "time" + + "github.com/aziontech/azion-cli/pkg/logger" + "go.uber.org/zap" +) + +// Response represents the structure of the response from the API. +type Response struct { + UUID string `json:"uuid"` + Image string `json:"image"` + Start time.Time `json:"start"` +} + +func callScript(token, id, secret, prefix, name string) (string, error) { + logger.Debug("Calling script runner api") + url := "https://stage-console.azion.com/api/template-engine/templates/92480a31-b88b-495b-8615-3ed5eff6314e/instantiate" + + // Define the request payload + payload := []map[string]string{ + { + "field": "PROJECT_NAME", + "instantiation_data_path": "envs.[0].value", + "value": name, + }, + { + "field": "B2_APP_KEY_ID", + "instantiation_data_path": "envs.[1].value", + "value": id, + }, + { + "field": "B2_APP_KEY", + "instantiation_data_path": "envs.[2].value", + "value": secret, + }, + { + "field": "PREFIX", + "instantiation_data_path": "envs.[3].value", + "value": prefix, + }, + } + + // Marshal the payload to JSON + jsonPayload, err := json.Marshal(payload) + if err != nil { + logger.Debug("Error marshalling payload", zap.Error(err)) + return "", err + } + + // Create a new HTTP request + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload)) + if err != nil { + logger.Debug("Error creating request", zap.Error(err)) + return "", err + } + + // Set headers + req.Header.Set("accept", "application/json; version=3") + req.Header.Set("content-type", "application/json; version=3") + req.Header.Set("Authorization", "Token "+token) + + // Send the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + logger.Debug("Error sending request", zap.Error(err)) + return "", err + } + defer resp.Body.Close() + + // Read the response + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + // Unmarshal the response body into the Response struct + var responseMap map[string]string + if err := json.Unmarshal(body, &responseMap); err != nil { + logger.Debug("Error unmarshalling response", zap.Error(err)) + return "", err + } + + return responseMap["uuid"], nil +} diff --git a/pkg/cmd/deploy/upload.go b/pkg/cmd/deploy/upload.go index 4c421e404..86965602a 100644 --- a/pkg/cmd/deploy/upload.go +++ b/pkg/cmd/deploy/upload.go @@ -15,19 +15,18 @@ import ( ) var ( - PathStatic = ".edge/storage" - Jobs chan contracts.FileOps - Retries int64 + Jobs chan contracts.FileOps + Retries int64 ) func (cmd *DeployCmd) uploadFiles( - f *cmdutil.Factory, conf *contracts.AzionApplicationOptions, msgs *[]string) error { + f *cmdutil.Factory, conf *contracts.AzionApplicationOptions, msgs *[]string, pathStatic string) error { // Get total amount of files to display progress totalFiles := 0 - if err := cmd.FilepathWalk(PathStatic, func(path string, info os.FileInfo, err error) error { + if err := cmd.FilepathWalk(pathStatic, func(path string, info os.FileInfo, err error) error { if err != nil { logger.Debug("Error while reading files to be uploaded", zap.Error(err)) - logger.Debug("File that caused the error: " + PathStatic) + logger.Debug("File that caused the error: " + pathStatic) return err } if !info.IsDir() { @@ -66,7 +65,7 @@ func (cmd *DeployCmd) uploadFiles( bar = nil } - if err := cmd.FilepathWalk(PathStatic, func(path string, info os.FileInfo, err error) error { + if err := cmd.FilepathWalk(pathStatic, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -78,7 +77,7 @@ func (cmd *DeployCmd) uploadFiles( return err } - fileString := strings.TrimPrefix(path, PathStatic) + fileString := strings.TrimPrefix(path, pathStatic) mimeType, err := mimemagic.MatchFilePath(path, -1) if err != nil { logger.Debug("Error while matching file path", zap.Error(err)) diff --git a/pkg/token/models.go b/pkg/token/models.go index 352441735..67cdc09d4 100644 --- a/pkg/token/models.go +++ b/pkg/token/models.go @@ -34,6 +34,9 @@ type Settings struct { ClientId string Email string ContinuationToken string + S3AccessKey string + S3SecreKey string + S3Bucket string } type Config struct {