Add peridotadmin and updateinfo workflow

This commit is contained in:
Mustafa Gezen 2023-02-17 19:28:05 +01:00
parent 2470a9df7f
commit 5eec7a5354
12 changed files with 825 additions and 0 deletions

View 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
View 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()
}

View 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
}

View 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
}

View 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"],
)

View 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"],
)

View File

@ -0,0 +1,6 @@
apiVersion: v2
name: peridotadmin
description: Helm chart for peridotadmin
type: application
version: 0.0.1
appVersion: "0.0.1"

View 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'),
})

View File

@ -0,0 +1,3 @@
# Ports under requires ingressHost to be set during deploy
http:
ingressHost: null

View 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)
}
}

View 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"],
)

View 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 {}