mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-20 17:58:29 +00:00
Add peridotadmin and updateinfo workflow
This commit is contained in:
parent
2470a9df7f
commit
5eec7a5354
26
peridot/admin/v1/BUILD.bazel
Normal file
26
peridot/admin/v1/BUILD.bazel
Normal file
@ -0,0 +1,26 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "admin",
|
||||
srcs = [
|
||||
"server.go",
|
||||
"updateinfo.go",
|
||||
],
|
||||
importpath = "peridot.resf.org/peridot/admin/v1",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//peridot/builder/v1:builder",
|
||||
"//peridot/db",
|
||||
"//peridot/impl/v1:impl",
|
||||
"//peridot/proto/v1:pb",
|
||||
"//peridot/proto/v1/admin:pb",
|
||||
"//proto:common",
|
||||
"//utils",
|
||||
"//vendor/github.com/sirupsen/logrus",
|
||||
"//vendor/go.temporal.io/sdk/client",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
"@org_golang_google_grpc//codes",
|
||||
"@org_golang_google_grpc//credentials/insecure",
|
||||
"@org_golang_google_grpc//status",
|
||||
],
|
||||
)
|
120
peridot/admin/v1/server.go
Normal file
120
peridot/admin/v1/server.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package peridotadminv1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.temporal.io/sdk/client"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
commonpb "peridot.resf.org/common"
|
||||
peridotadminpb "peridot.resf.org/peridot/admin/pb"
|
||||
builderv1 "peridot.resf.org/peridot/builder/v1"
|
||||
peridotdb "peridot.resf.org/peridot/db"
|
||||
peridotimplv1 "peridot.resf.org/peridot/impl/v1"
|
||||
"peridot.resf.org/utils"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
peridotadminpb.UnimplementedPeridotAdminServiceServer
|
||||
|
||||
log *logrus.Logger
|
||||
db peridotdb.Access
|
||||
temporal client.Client
|
||||
temporalWorker *builderv1.Worker
|
||||
}
|
||||
|
||||
var adminUser = &utils.ContextUser{
|
||||
ID: "peridot-errata",
|
||||
AuthToken: "",
|
||||
Name: "Peridot Errata",
|
||||
Email: "releng+errata@rockylinux.org",
|
||||
}
|
||||
|
||||
func NewServer(db peridotdb.Access, c client.Client) (*Server, error) {
|
||||
temporalWorker, err := builderv1.NewWorker(db, c, peridotimplv1.MainTaskQueue, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Server{
|
||||
log: logrus.New(),
|
||||
db: db,
|
||||
temporal: c,
|
||||
temporalWorker: temporalWorker,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) interceptor(ctx context.Context, req interface{}, usi *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
n := utils.EndInterceptor
|
||||
|
||||
return n(ctx, req, usi, handler)
|
||||
}
|
||||
|
||||
func (s *Server) serverInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
n := utils.ServerEndInterceptor
|
||||
|
||||
return n(srv, ss, info, handler)
|
||||
}
|
||||
|
||||
func (s *Server) Run() {
|
||||
res := utils.NewGRPCServer(
|
||||
&utils.GRPCOptions{
|
||||
DialOptions: []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
},
|
||||
Interceptor: s.interceptor,
|
||||
ServerInterceptor: s.serverInterceptor,
|
||||
},
|
||||
func(r *utils.Register) {
|
||||
endpoints := []utils.GrpcEndpointRegister{
|
||||
commonpb.RegisterHealthCheckServiceHandlerFromEndpoint,
|
||||
peridotadminpb.RegisterPeridotAdminServiceHandlerFromEndpoint,
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
err := endpoint(r.Context, r.Mux, r.Endpoint, r.Options)
|
||||
if err != nil {
|
||||
s.log.Fatalf("could not register handler - %v", err)
|
||||
}
|
||||
}
|
||||
},
|
||||
func(r *utils.RegisterServer) {
|
||||
commonpb.RegisterHealthCheckServiceServer(r.Server, &utils.HealthServer{})
|
||||
|
||||
peridotadminpb.RegisterPeridotAdminServiceServer(r.Server, s)
|
||||
},
|
||||
)
|
||||
|
||||
defer res.Cancel()
|
||||
res.WaitGroup.Wait()
|
||||
}
|
99
peridot/admin/v1/updateinfo.go
Normal file
99
peridot/admin/v1/updateinfo.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package peridotadminv1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.temporal.io/sdk/client"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
adminpb "peridot.resf.org/peridot/admin/pb"
|
||||
peridotpb "peridot.resf.org/peridot/pb"
|
||||
"peridot.resf.org/utils"
|
||||
)
|
||||
|
||||
func (s *Server) AddUpdateInformation(ctx context.Context, req *adminpb.AddUpdateInformationRequest) (*peridotpb.AsyncTask, error) {
|
||||
if err := req.ValidateAll(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rollback := true
|
||||
beginTx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
s.log.Error(err)
|
||||
return nil, utils.InternalError
|
||||
}
|
||||
defer func() {
|
||||
if rollback {
|
||||
_ = beginTx.Rollback()
|
||||
}
|
||||
}()
|
||||
tx := s.db.UseTransaction(beginTx)
|
||||
|
||||
task, err := tx.CreateTask(adminUser, "noarch", peridotpb.TaskType_TASK_TYPE_UPDATEINFO, &req.ProjectId, nil)
|
||||
if err != nil {
|
||||
s.log.Errorf("could not create task: %v", err)
|
||||
return nil, utils.InternalError
|
||||
}
|
||||
|
||||
taskProto, err := task.ToProto(false)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not marshal task: %v", err)
|
||||
}
|
||||
|
||||
rollback = false
|
||||
err = beginTx.Commit()
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "could not save, try again")
|
||||
}
|
||||
|
||||
_, err = s.temporal.ExecuteWorkflow(
|
||||
context.Background(),
|
||||
client.StartWorkflowOptions{
|
||||
ID: task.ID.String(),
|
||||
TaskQueue: "yumrepofs",
|
||||
},
|
||||
s.temporalWorker.WorkflowController.UpdateInfoWorkflow,
|
||||
req,
|
||||
task,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Errorf("could not start sync workflow in AddUpdateInformation: %v", err)
|
||||
_ = s.db.SetTaskStatus(task.ID.String(), peridotpb.TaskStatus_TASK_STATUS_FAILED)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &peridotpb.AsyncTask{
|
||||
TaskId: task.ID.String(),
|
||||
Subtasks: []*peridotpb.Subtask{taskProto},
|
||||
Done: false,
|
||||
}, nil
|
||||
}
|
302
peridot/builder/v1/workflow/updateinfo.go
Normal file
302
peridot/builder/v1/workflow/updateinfo.go
Normal file
@ -0,0 +1,302 @@
|
||||
// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cirello.io/dynamolock"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/viper"
|
||||
"go.temporal.io/sdk/temporal"
|
||||
"go.temporal.io/sdk/workflow"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
adminpb "peridot.resf.org/peridot/admin/pb"
|
||||
"peridot.resf.org/peridot/db/models"
|
||||
peridotpb "peridot.resf.org/peridot/pb"
|
||||
"peridot.resf.org/peridot/yummeta"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Controller) SetUpdateInfoActivity(ctx context.Context, req *adminpb.AddUpdateInformationRequest) (*adminpb.AddUpdateInformationTask, error) {
|
||||
stopChan := makeHeartbeat(ctx, 10*time.Second)
|
||||
defer func() { stopChan <- true }()
|
||||
|
||||
lock, err := dynamolock.New(
|
||||
c.dynamodb,
|
||||
viper.GetString("dynamodb-table"),
|
||||
dynamolock.WithLeaseDuration(10*time.Second),
|
||||
dynamolock.WithHeartbeatPeriod(3*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create dynamolock: %v", err)
|
||||
}
|
||||
defer lock.Close()
|
||||
|
||||
var lockedItem *dynamolock.Lock
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
lockedItem, err = lock.AcquireLock(
|
||||
req.ProjectId,
|
||||
)
|
||||
if err != nil {
|
||||
c.log.Errorf("failed to acquire lock: %v", err)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
didRelease := false
|
||||
releaseLock := func() error {
|
||||
if didRelease {
|
||||
return nil
|
||||
}
|
||||
lockSuccess, err := lock.ReleaseLock(lockedItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error releasing lock: %v", err)
|
||||
}
|
||||
if !lockSuccess {
|
||||
return fmt.Errorf("lost lock before release")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer releaseLock()
|
||||
|
||||
projects, err := c.db.ListProjects(&peridotpb.ProjectFilters{
|
||||
Id: wrapperspb.String(req.ProjectId),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list projects: %v", err)
|
||||
}
|
||||
if len(projects) == 0 {
|
||||
return nil, fmt.Errorf("project not found")
|
||||
}
|
||||
project := projects[0]
|
||||
|
||||
repositories, err := c.db.FindRepositoriesForProject(project.ID.String(), nil, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list repositories: %v", err)
|
||||
}
|
||||
|
||||
apiBase := "https://apollo.build.resf.org/api/v3/updateinfo"
|
||||
|
||||
for _, repo := range repositories {
|
||||
if repo.Name == "all" {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, arch := range project.Archs {
|
||||
realProductName := strings.ReplaceAll(req.ProductName, "$arch", arch)
|
||||
apiURL := fmt.Sprintf(
|
||||
"%s/%s/%s/updateinfo.xml",
|
||||
apiBase,
|
||||
url.PathEscape(realProductName),
|
||||
repo.Name,
|
||||
)
|
||||
c.log.Infof("Getting updateinfo %s", apiURL)
|
||||
resp, err := http.Get(apiURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get updateinfo: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
c.log.Warnf("no updateinfo found for %s/%s", realProductName, repo.Name)
|
||||
continue
|
||||
} else {
|
||||
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
c.log.Infof("Got updateinfo for %s/%s", realProductName, repo.Name)
|
||||
|
||||
xmlBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read updateinfo: %v", err)
|
||||
}
|
||||
|
||||
hasher := sha256.New()
|
||||
|
||||
openSize := len(xmlBytes)
|
||||
_, err = hasher.Write(xmlBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash updateinfo: %v", err)
|
||||
}
|
||||
openChecksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
hasher.Reset()
|
||||
|
||||
var gzippedBuf bytes.Buffer
|
||||
w := gzip.NewWriter(&gzippedBuf)
|
||||
_, err = w.Write(xmlBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not gzip encode: %v", err)
|
||||
}
|
||||
_ = w.Close()
|
||||
|
||||
closedSize := len(gzippedBuf.Bytes())
|
||||
_, err = hasher.Write(gzippedBuf.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash gzipped: %v", err)
|
||||
}
|
||||
closedChecksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
hasher.Reset()
|
||||
updateInfoB64 := base64.StdEncoding.EncodeToString(gzippedBuf.Bytes())
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
newRevisionID := uuid.New()
|
||||
updateInfoPath := fmt.Sprintf("repodata/%s-UPDATEINFO.xml.gz", newRevisionID.String())
|
||||
updateInfoEntry := &yummeta.RepoMdData{
|
||||
Type: "updateinfo",
|
||||
Checksum: &yummeta.RepoMdDataChecksum{
|
||||
Type: "sha256",
|
||||
Value: closedChecksum,
|
||||
},
|
||||
OpenChecksum: &yummeta.RepoMdDataChecksum{
|
||||
Type: "sha256",
|
||||
Value: openChecksum,
|
||||
},
|
||||
Location: &yummeta.RepoMdDataLocation{
|
||||
Href: updateInfoPath,
|
||||
},
|
||||
Timestamp: timestamp,
|
||||
Size: closedSize,
|
||||
OpenSize: openSize,
|
||||
}
|
||||
|
||||
latestRevision, err := c.db.GetLatestActiveRepositoryRevision(repo.ID.String(), arch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get latest active repository revision: %v", err)
|
||||
}
|
||||
|
||||
// Get the existing repomd.xml
|
||||
// If updateinfo already exists, replace it
|
||||
// Else append it
|
||||
repomdXml, err := base64.StdEncoding.DecodeString(latestRevision.RepomdXml)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode repomd xml: %w", err)
|
||||
}
|
||||
var repomdRoot yummeta.RepoMdRoot
|
||||
err = xml.Unmarshal(repomdXml, &repomdRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal repomd.xml: %v", err)
|
||||
}
|
||||
|
||||
doesExist := false
|
||||
for i, existingEntry := range repomdRoot.Data {
|
||||
if existingEntry.Type == "updateinfo" {
|
||||
repomdRoot.Data[i] = updateInfoEntry
|
||||
doesExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !doesExist {
|
||||
repomdRoot.Data = append(repomdRoot.Data, updateInfoEntry)
|
||||
}
|
||||
|
||||
// Re-marshal the repomd.xml
|
||||
repomdRoot.Revision = newRevisionID.String()
|
||||
repomdXml, err = xml.Marshal(repomdRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal repomd.xml: %v", err)
|
||||
}
|
||||
repomdXmlB64 := base64.StdEncoding.EncodeToString(repomdXml)
|
||||
|
||||
// Create new revision
|
||||
_, err = c.db.CreateRevisionForRepository(
|
||||
newRevisionID.String(),
|
||||
latestRevision.ProjectRepoId,
|
||||
latestRevision.Arch,
|
||||
repomdXmlB64,
|
||||
latestRevision.PrimaryXml,
|
||||
latestRevision.FilelistsXml,
|
||||
latestRevision.OtherXml,
|
||||
updateInfoB64,
|
||||
latestRevision.ModuleDefaultsYaml,
|
||||
latestRevision.ModulesYaml,
|
||||
latestRevision.GroupsXml,
|
||||
latestRevision.UrlMappings.String(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating new revision: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &adminpb.AddUpdateInformationTask{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) UpdateInfoWorkflow(ctx workflow.Context, req *adminpb.AddUpdateInformationRequest, task *models.Task) (*adminpb.AddUpdateInformationTask, error) {
|
||||
var ret adminpb.AddUpdateInformationTask
|
||||
|
||||
deferTask, errorDetails, err := c.commonCreateTask(task, &ret)
|
||||
defer deferTask()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// should fall back to FAILED in case it actually fails before we
|
||||
// can set it to SUCCEEDED
|
||||
task.Status = peridotpb.TaskStatus_TASK_STATUS_FAILED
|
||||
|
||||
stage2Ctx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
||||
ScheduleToStartTimeout: 25 * time.Hour,
|
||||
StartToCloseTimeout: 30 * time.Hour,
|
||||
HeartbeatTimeout: 30 * time.Second,
|
||||
TaskQueue: c.mainQueue,
|
||||
// Yumrepofs is locking for a short period so let's not wait too long to retry
|
||||
RetryPolicy: &temporal.RetryPolicy{
|
||||
InitialInterval: 5 * time.Second,
|
||||
BackoffCoefficient: 1.1,
|
||||
MaximumInterval: 25 * time.Second,
|
||||
},
|
||||
})
|
||||
err = workflow.ExecuteActivity(stage2Ctx, c.SetUpdateInfoActivity, req).Get(stage2Ctx, &ret)
|
||||
if err != nil {
|
||||
setActivityError(errorDetails, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.Status = peridotpb.TaskStatus_TASK_STATUS_SUCCEEDED
|
||||
|
||||
return &ret, nil
|
||||
}
|
24
peridot/cmd/v1/peridotadmin/BUILD.bazel
Normal file
24
peridot/cmd/v1/peridotadmin/BUILD.bazel
Normal file
@ -0,0 +1,24 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "peridotadmin_lib",
|
||||
srcs = ["main.go"],
|
||||
importpath = "peridot.resf.org/peridot/cmd/v1/peridotadmin",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//peridot/admin/v1:admin",
|
||||
"//peridot/common",
|
||||
"//peridot/db/connector",
|
||||
"//temporalutils",
|
||||
"//utils",
|
||||
"//vendor/github.com/sirupsen/logrus",
|
||||
"//vendor/github.com/spf13/cobra",
|
||||
"//vendor/go.temporal.io/sdk/client",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "peridotadmin",
|
||||
embed = [":peridotadmin_lib"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
18
peridot/cmd/v1/peridotadmin/ci/BUILD.bazel
Normal file
18
peridot/cmd/v1/peridotadmin/ci/BUILD.bazel
Normal file
@ -0,0 +1,18 @@
|
||||
load("//rules_resf:defs.bzl", "RESFDEPLOY_OUTS_MIGRATE", "container", "peridot_k8s")
|
||||
|
||||
container(
|
||||
base = "//bases/bazel/go",
|
||||
files = [
|
||||
"//peridot/cmd/v1/peridotadmin",
|
||||
],
|
||||
image_name = "peridotadmin",
|
||||
)
|
||||
|
||||
peridot_k8s(
|
||||
name = "peridotadmin",
|
||||
src = "deploy.jsonnet",
|
||||
outs = RESFDEPLOY_OUTS_MIGRATE,
|
||||
chart_yaml = "Chart.yaml",
|
||||
values_yaml = "values.yaml",
|
||||
deps = ["//ci"],
|
||||
)
|
6
peridot/cmd/v1/peridotadmin/ci/Chart.yaml
Normal file
6
peridot/cmd/v1/peridotadmin/ci/Chart.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: peridotadmin
|
||||
description: Helm chart for peridotadmin
|
||||
type: application
|
||||
version: 0.0.1
|
||||
appVersion: "0.0.1"
|
75
peridot/cmd/v1/peridotadmin/ci/deploy.jsonnet
Normal file
75
peridot/cmd/v1/peridotadmin/ci/deploy.jsonnet
Normal file
@ -0,0 +1,75 @@
|
||||
local resfdeploy = import 'ci/resfdeploy.jsonnet';
|
||||
local db = import 'ci/db.jsonnet';
|
||||
local kubernetes = import 'ci/kubernetes.jsonnet';
|
||||
local temporal = import 'ci/temporal.jsonnet';
|
||||
local utils = import 'ci/utils.jsonnet';
|
||||
local s3 = import 'ci/s3.jsonnet';
|
||||
|
||||
resfdeploy.new({
|
||||
name: 'peridotadmin',
|
||||
helm_strip_prefix: 'PERIDOTADMIN_',
|
||||
replicas: 1,
|
||||
dbname: 'peridot',
|
||||
backend: true,
|
||||
migrate: true,
|
||||
migrate_command: ['/bin/sh'],
|
||||
migrate_args: ['-c', 'exit 0'],
|
||||
legacyDb: true,
|
||||
command: '/bundle/peridotadmin',
|
||||
image: kubernetes.tag('peridotadmin'),
|
||||
tag: kubernetes.version,
|
||||
dsn: {
|
||||
name: 'PERIDOTADMIN_DATABASE_URL',
|
||||
value: db.dsn_legacy('peridot', false, 'peridotadmin'),
|
||||
},
|
||||
requests: if kubernetes.prod() then {
|
||||
cpu: '0.2',
|
||||
memory: '512M',
|
||||
},
|
||||
limits: if kubernetes.prod() then {
|
||||
cpu: '2',
|
||||
memory: '12G',
|
||||
} else {
|
||||
cpu: '2',
|
||||
memory: '10G',
|
||||
},
|
||||
service_account_options: {
|
||||
annotations: {
|
||||
'eks.amazonaws.com/role-arn': if utils.helm_mode then '{{ .Values.awsRoleArn | default !"!" }}' else 'arn:aws:iam::893168113496:role/peridot_k8s_role',
|
||||
}
|
||||
},
|
||||
ports: [
|
||||
{
|
||||
name: 'http',
|
||||
containerPort: 15012,
|
||||
protocol: 'TCP',
|
||||
},
|
||||
{
|
||||
name: 'grpc',
|
||||
containerPort: 15013,
|
||||
protocol: 'TCP',
|
||||
},
|
||||
],
|
||||
health: {
|
||||
port: 15012,
|
||||
},
|
||||
env: [
|
||||
{
|
||||
name: 'PERIDOTADMIN_PRODUCTION',
|
||||
value: if kubernetes.dev() then 'false' else 'true',
|
||||
},
|
||||
{
|
||||
name: 'HYDRA_PUBLIC_HTTP_ENDPOINT_OVERRIDE',
|
||||
value: if utils.helm_mode then '{{ .Values.hydraPublicEndpoint | default !"!" }}' else '',
|
||||
},
|
||||
{
|
||||
name: 'HYDRA_ADMIN_HTTP_ENDPOINT_OVERRIDE',
|
||||
value: if utils.helm_mode then '{{ .Values.hydraAdminEndpoint | default !"!" }}' else '',
|
||||
},
|
||||
{
|
||||
name: 'SPICEDB_GRPC_ENDPOINT_OVERRIDE',
|
||||
value: if utils.helm_mode then '{{ .Values.spicedbEndpoint | default !"!" }}' else '',
|
||||
},
|
||||
$.dsn,
|
||||
] + s3.kube_env('PERIDOTADMIN') + temporal.kube_env('PERIDOTADMIN'),
|
||||
})
|
3
peridot/cmd/v1/peridotadmin/ci/values.yaml
Normal file
3
peridot/cmd/v1/peridotadmin/ci/values.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
# Ports under requires ingressHost to be set during deploy
|
||||
http:
|
||||
ingressHost: null
|
81
peridot/cmd/v1/peridotadmin/main.go
Normal file
81
peridot/cmd/v1/peridotadmin/main.go
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
|
||||
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.temporal.io/sdk/client"
|
||||
peridotadminv1 "peridot.resf.org/peridot/admin/v1"
|
||||
peridotcommon "peridot.resf.org/peridot/common"
|
||||
serverconnector "peridot.resf.org/peridot/db/connector"
|
||||
"peridot.resf.org/temporalutils"
|
||||
"peridot.resf.org/utils"
|
||||
)
|
||||
|
||||
var root = &cobra.Command{
|
||||
Use: "peridotadmin",
|
||||
Run: mn,
|
||||
}
|
||||
|
||||
var cnf = utils.NewFlagConfig()
|
||||
|
||||
func init() {
|
||||
cnf.DefaultPort = 15012
|
||||
|
||||
dname := "peridot"
|
||||
cnf.DatabaseName = &dname
|
||||
cnf.Name = "peridotadmin"
|
||||
|
||||
peridotcommon.AddFlags(root.PersistentFlags())
|
||||
utils.AddFlags(root.PersistentFlags(), cnf)
|
||||
}
|
||||
|
||||
func mn(_ *cobra.Command, _ []string) {
|
||||
c, err := temporalutils.NewClient(client.Options{})
|
||||
if err != nil {
|
||||
logrus.Fatalln("unable to create Temporal client", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
s, err := peridotadminv1.NewServer(serverconnector.MustAuto(), c)
|
||||
if err != nil {
|
||||
logrus.Fatalf("could not init server: %v", err)
|
||||
}
|
||||
s.Run()
|
||||
}
|
||||
|
||||
func main() {
|
||||
utils.Main()
|
||||
if err := root.Execute(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
41
peridot/proto/v1/admin/BUILD.bazel
Normal file
41
peridot/proto/v1/admin/BUILD.bazel
Normal file
@ -0,0 +1,41 @@
|
||||
load("@rules_proto//proto:defs.bzl", "proto_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
|
||||
|
||||
proto_library(
|
||||
name = "adminpb_proto",
|
||||
srcs = ["admin.proto"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//apollo/proto/v1:apollopb_proto",
|
||||
"//peridot/proto/v1:peridotpb_proto",
|
||||
"@com_envoyproxy_protoc_gen_validate//validate:validate_proto",
|
||||
"@go_googleapis//google/api:annotations_proto",
|
||||
],
|
||||
)
|
||||
|
||||
go_proto_library(
|
||||
name = "adminpb_go_proto",
|
||||
compilers = [
|
||||
"//:go_apiv2",
|
||||
"//:go_grpc",
|
||||
"//:go_validate",
|
||||
"@com_github_grpc_ecosystem_grpc_gateway_v2//protoc-gen-grpc-gateway:go_gen_grpc_gateway",
|
||||
],
|
||||
importpath = "peridot.resf.org/peridot/admin/pb",
|
||||
proto = ":adminpb_proto",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//apollo/proto/v1:pb",
|
||||
"//peridot/proto/v1:pb",
|
||||
"@com_envoyproxy_protoc_gen_validate//validate:validate_go_proto",
|
||||
"@go_googleapis//google/api:annotations_go_proto",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "pb",
|
||||
embed = [":adminpb_go_proto"],
|
||||
importpath = "peridot.resf.org/peridot/admin/pb",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
30
peridot/proto/v1/admin/admin.proto
Normal file
30
peridot/proto/v1/admin/admin.proto
Normal file
@ -0,0 +1,30 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package resf.peridot.admin.v1;
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "apollo/proto/v1/advisory.proto";
|
||||
import "peridot/proto/v1/task.proto";
|
||||
|
||||
option go_package = "peridot.resf.org/peridot/admin/pb;adminpb";
|
||||
|
||||
service PeridotAdminService {
|
||||
rpc AddUpdateInformation (AddUpdateInformationRequest) returns (resf.peridot.v1.AsyncTask) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/admin/add_update_information"
|
||||
body: "*"
|
||||
};
|
||||
option (resf.peridot.v1.task_info) = {
|
||||
response_type: "AddUpdateInformationTask"
|
||||
metadata_type: "AddUpdateInformationRequest"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message AddUpdateInformationRequest {
|
||||
string project_id = 1 [(validate.rules).string.min_len = 1];
|
||||
string product_name = 2 [(validate.rules).string.min_len = 1];
|
||||
}
|
||||
|
||||
message AddUpdateInformationTask {}
|
Loading…
Reference in New Issue
Block a user