fix dns, build image and run image
This commit is contained in:
parent
df4bfd8517
commit
e143009677
|
@ -0,0 +1,25 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||
|
||||
"github.com/google/go-github/v39/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
CheckWebhook(ctx context.Context) error
|
||||
UpdateCommitStatus(ctx context.Context, number int, sha, status, desc, targetURL string) error
|
||||
GetPullRequests(p int) ([]*github.PullRequest, error)
|
||||
}
|
||||
|
||||
func NewClient(token *oauth2.Token) (Client, error) {
|
||||
if settings.ServiceType == "github" {
|
||||
return NewGithubClient(token), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported service type: %s", settings.ServiceType)
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package services
|
|
@ -0,0 +1,18 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||
)
|
||||
|
||||
func getPRDir(number int) string {
|
||||
return filepath.Join(settings.CodeCacheDir, strconv.Itoa(number))
|
||||
}
|
||||
|
||||
func WithDir(cmd *exec.Cmd, dir string) *exec.Cmd {
|
||||
cmd.Dir = dir
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func getImageTag(number int) string {
|
||||
return fmt.Sprintf("pr-%d", number)
|
||||
}
|
||||
|
||||
// buildImage build the docker image via Dockerfile
|
||||
func buildImage(ctx context.Context, number int) (string, error) {
|
||||
p := getPRDir(number)
|
||||
tagName := fmt.Sprintf("%s/%s:%s", settings.RepoOwner, settings.RepoName, getImageTag(number))
|
||||
cmd := exec.Command("docker", "build", "-t",
|
||||
tagName,
|
||||
".",
|
||||
)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, "docker build")
|
||||
}
|
||||
|
||||
return tagName, nil
|
||||
}
|
||||
|
||||
func runImage(ctx context.Context, number int, image string) (string, error) {
|
||||
p := getPRDir(number)
|
||||
var buf bytes.Buffer
|
||||
cmd := exec.Command("docker", "run", "-d", image)
|
||||
cmd.Stdout = &buf
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, "docker run")
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||
"github.com/cloudflare/cloudflare-go"
|
||||
"github.com/google/go-github/v39/github"
|
||||
)
|
||||
|
||||
func checkAndUpdateSubDomain(number int) error {
|
||||
api, err := cloudflare.NewWithAPIToken(settings.CloudflareToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zoneID, err := api.ZoneIDByName(settings.PRParentDomain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ZoneIDByName: %v", err)
|
||||
}
|
||||
|
||||
var found bool
|
||||
var name = fmt.Sprintf("try-pr-%d", number)
|
||||
var filter = cloudflare.DNSRecord{
|
||||
Type: "A",
|
||||
Name: name + "." + settings.PRParentDomain,
|
||||
}
|
||||
recs, err := api.DNSRecords(context.Background(), zoneID, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DNSRecords: %v", err)
|
||||
}
|
||||
|
||||
for _, r := range recs {
|
||||
if filter.Name == r.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
// TODO: check ip address
|
||||
return nil
|
||||
}
|
||||
|
||||
asciiInput := cloudflare.DNSRecord{
|
||||
Type: "A",
|
||||
Name: name,
|
||||
Content: settings.DomainIP,
|
||||
Proxiable: false,
|
||||
Proxied: github.Bool(false),
|
||||
}
|
||||
|
||||
_, err = api.CreateDNSRecord(context.Background(), zoneID, asciiInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CreateDNSRecord: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func updateGitRepo(number int, sha string) (string, error) {
|
||||
p := getPRDir(number)
|
||||
log.Trace("clone code into", p)
|
||||
|
||||
var local = fmt.Sprintf("pull/%d/head:pr/%d", number, number)
|
||||
st, err := os.Stat(p)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := exec.Command("git", "clone", settings.RepoURL, p).Run(); err != nil {
|
||||
return "", fmt.Errorf("git clone: %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "fetch", "origin", local)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, "fetch")
|
||||
}
|
||||
} else if !st.IsDir() {
|
||||
return "", fmt.Errorf("%s is a file but not a dir", p)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "pull", "origin", local)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("checkout %s", sha))
|
||||
}
|
||||
|
||||
var branchName = fmt.Sprintf("pr/%d", number)
|
||||
var newSHA = sha
|
||||
|
||||
if sha != "" {
|
||||
cmd := exec.Command("git", "checkout", sha)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("checkout %s", sha))
|
||||
}
|
||||
newSHA = sha
|
||||
} else {
|
||||
cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
|
||||
cmd.Dir = p
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("rev-parse --abbrev-ref: %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != branchName {
|
||||
cmd := exec.Command("git", "checkout", branchName)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", fmt.Errorf("git checkout %s: %v", branchName, err)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = exec.Command("git", "rev-parse", "HEAD")
|
||||
cmd.Dir = p
|
||||
var buf2 bytes.Buffer
|
||||
cmd.Stdout = &buf2
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("git rev-parse Head: %v", err)
|
||||
}
|
||||
newSHA = strings.TrimSpace(buf2.String())
|
||||
}
|
||||
|
||||
return newSHA, nil
|
||||
}
|
|
@ -18,12 +18,6 @@ import (
|
|||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
CheckWebhook(ctx context.Context) error
|
||||
UpdateCommitStatus(ctx context.Context, number int, sha, status, desc, targetURL string) error
|
||||
GetPullRequests(p int) ([]*github.PullRequest, error)
|
||||
}
|
||||
|
||||
type GithubClient struct {
|
||||
client *github.Client
|
||||
}
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.com/gitea/pr-deployer/pkgs/settings"
|
||||
|
||||
"github.com/cloudflare/cloudflare-go"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const (
|
||||
statusPending = "pending"
|
||||
statusSuccess = "success"
|
||||
statusError = "error"
|
||||
statusFailure = "failure"
|
||||
)
|
||||
|
||||
func UpdateAndStartPullRequest(ctx context.Context, client Client, number int, sha string) error {
|
||||
|
@ -59,200 +53,27 @@ func updateAndStartPullRequest(ctx context.Context, client Client, number int, n
|
|||
|
||||
log.Trace("buildImage")
|
||||
// 3 build
|
||||
if err := buildImage(ctx, number); err != nil {
|
||||
image, err := buildImage(ctx, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("runImage")
|
||||
log.Trace("runImage", image)
|
||||
// 4 change reverse server
|
||||
if err := runImage(ctx, number); err != nil {
|
||||
runID, err := runImage(ctx, number, image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("UpdateCommitStatus success", newSHA)
|
||||
log.Trace("UpdateCommitStatus success", newSHA, runID)
|
||||
// 5 send commit status
|
||||
if err := client.UpdateCommitStatus(ctx, number, newSHA, statusSuccess, "", fmt.Sprintf("https://try-pr-%d.gitea.io", number)); err != nil {
|
||||
if err := client.UpdateCommitStatus(ctx, number, newSHA, statusSuccess, "", fmt.Sprintf("https://try-pr-%d.%s", number, settings.PRParentDomain)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runImage(ctx context.Context, number int) error {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: stop the old container
|
||||
|
||||
// start the new container
|
||||
_, err = cli.ContainerExecCreate(ctx, fmt.Sprintf("%s/%s:%s", settings.RepoOwner, settings.RepoName, getImageTag(number)), types.ExecConfig{
|
||||
WorkingDir: getPRDir(number),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
statusPending = "pending"
|
||||
statusSuccess = "success"
|
||||
statusError = "error"
|
||||
statusFailure = "failure"
|
||||
)
|
||||
|
||||
func getPRDir(number int) string {
|
||||
return filepath.Join(settings.CodeCacheDir, strconv.Itoa(number))
|
||||
}
|
||||
|
||||
func WithDir(cmd *exec.Cmd, dir string) *exec.Cmd {
|
||||
cmd.Dir = dir
|
||||
return cmd
|
||||
}
|
||||
|
||||
func updateGitRepo(number int, sha string) (string, error) {
|
||||
p := getPRDir(number)
|
||||
log.Trace("clone code into", p)
|
||||
|
||||
var local = fmt.Sprintf("pull/%d/head:pr/%d", number, number)
|
||||
st, err := os.Stat(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := exec.Command("git", "clone", settings.RepoURL, p).Run(); err != nil {
|
||||
return "", fmt.Errorf("git clone: %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "fetch", "origin", local)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, "fetch")
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
if !st.IsDir() {
|
||||
return "", fmt.Errorf("%s is a file but not a dir", p)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "pull", "origin", local)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("checkout %s", sha))
|
||||
}
|
||||
|
||||
var branchName = fmt.Sprintf("pr/%d", number)
|
||||
var newSHA = sha
|
||||
|
||||
if sha != "" {
|
||||
cmd := exec.Command("git", "checkout", sha)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", errors.Wrap(err, fmt.Sprintf("checkout %s", sha))
|
||||
}
|
||||
newSHA = sha
|
||||
} else {
|
||||
cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
|
||||
cmd.Dir = p
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("rev-parse --abbrev-ref: %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != branchName {
|
||||
cmd := exec.Command("git", "checkout", branchName)
|
||||
if err := WithDir(cmd, p).Run(); err != nil {
|
||||
return "", fmt.Errorf("git checkout %s: %v", branchName, err)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = exec.Command("git", "rev-parse", "HEAD")
|
||||
cmd.Dir = p
|
||||
var buf2 bytes.Buffer
|
||||
cmd.Stdout = &buf2
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("git rev-parse Head: %v", err)
|
||||
}
|
||||
newSHA = strings.TrimSpace(buf2.String())
|
||||
}
|
||||
|
||||
return newSHA, nil
|
||||
}
|
||||
|
||||
func getImageTag(number int) string {
|
||||
return fmt.Sprintf("pr-%d", number)
|
||||
}
|
||||
|
||||
// buildImage build the docker image via Dockerfile
|
||||
func buildImage(ctx context.Context, number int) error {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buildContext io.Reader
|
||||
if _, err = cli.ImageBuild(ctx, buildContext, types.ImageBuildOptions{
|
||||
Tags: []string{getImageTag(number)},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkAndUpdateSubDomain(number int) error {
|
||||
api, err := cloudflare.NewWithAPIToken(settings.CloudflareToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zoneID, err := api.ZoneIDByName("gitea.io")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var found bool
|
||||
var name = fmt.Sprintf("try-pr-%d.gitea.io", number)
|
||||
foo := cloudflare.DNSRecord{
|
||||
Type: "name",
|
||||
Name: name,
|
||||
}
|
||||
recs, err := api.DNSRecords(context.Background(), zoneID, foo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range recs {
|
||||
if name == r.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
// check ip address
|
||||
return nil
|
||||
}
|
||||
|
||||
asciiInput := cloudflare.DNSRecord{
|
||||
Type: "A",
|
||||
Name: name,
|
||||
Content: settings.DomainIP,
|
||||
TTL: 120,
|
||||
//Priority: &priority,
|
||||
//Proxied: &proxied,
|
||||
}
|
||||
|
||||
_, err = api.CreateDNSRecord(context.Background(), zoneID, asciiInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopPullRequest(number int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewClient(token *oauth2.Token) (Client, error) {
|
||||
if settings.ServiceType == "github" {
|
||||
return NewGithubClient(token), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported service type: %s", settings.ServiceType)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ var (
|
|||
OAuth2ClientSecret string
|
||||
Domain string
|
||||
DomainIP string
|
||||
PRParentDomain string
|
||||
WebhookSecretKey []byte
|
||||
CodeCacheDir string
|
||||
CloudflareToken string
|
||||
|
@ -104,6 +105,11 @@ func Init() error {
|
|||
|
||||
Domain = viper.GetString("Domain")
|
||||
DomainIP = viper.GetString("DomainIP")
|
||||
PRParentDomain = viper.GetString("PRParentDomain")
|
||||
if PRParentDomain == "" {
|
||||
return errors.New("PRParentDomain should not be empty")
|
||||
}
|
||||
|
||||
WebhookSecretKey = []byte(viper.GetString("WebhookSecretKey"))
|
||||
CodeCacheDir = viper.GetString("CodeCacheDir")
|
||||
CodeCacheDir, err = filepath.Abs(CodeCacheDir)
|
||||
|
|
Loading…
Reference in New Issue