Documentation
¶
Overview ¶
Example ¶
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/entur/go-logging"
"github.com/entur/go-orchestrator"
"github.com/entur/go-orchestrator/oresources"
)
type ExampleSpecV1 struct {
Name string `json:"name"`
}
type ExampleMetadataV1 struct {
ID string `json:"id"`
}
type ExampleManifestV1 struct {
orchestrator.ManifestHeader
Metadata ExampleMetadataV1 `json:"metadata"`
Spec ExampleSpecV1 `json:"spec"`
}
type ExampleManifestV1Handler struct {
/* you can have some internal state here */
}
func (h *ExampleManifestV1Handler) APIVersion() orchestrator.APIVersion {
return "orchestrator.entur.io/example/v1"
}
func (h *ExampleManifestV1Handler) Kind() orchestrator.Kind {
return "Example"
}
func (h *ExampleManifestV1Handler) MiddlewareBefore(ctx context.Context, req orchestrator.Request, r *orchestrator.Result) error {
logger := logging.Ctx(ctx)
logger.Info().Msg("After Orchestrator middleware executes, but before manifest handler executes")
return nil
}
func (h *ExampleManifestV1Handler) Plan(ctx context.Context, req orchestrator.Request, r *orchestrator.Result) error {
var manifest ExampleManifestV1
err := json.Unmarshal(req.Manifest.New, &manifest)
if err != nil {
return err
}
r.Create("A thing")
r.Update("A thing")
r.Delete("A thing")
r.Succeed("Plan all the things")
return nil
}
func (h *ExampleManifestV1Handler) PlanDestroy(ctx context.Context, req orchestrator.Request, r *orchestrator.Result) error {
return fmt.Errorf("plandestroy not implemented")
}
func (h *ExampleManifestV1Handler) Apply(ctx context.Context, req orchestrator.Request, r *orchestrator.Result) error {
var manifest ExampleManifestV1
err := json.Unmarshal(req.Manifest.New, &manifest)
if err != nil {
return err
}
r.Create("A thing")
r.Update("A thing")
r.Delete("A thing")
r.Succeed("Plan all the things")
return nil
}
func (h *ExampleManifestV1Handler) Destroy(ctx context.Context, req orchestrator.Request, r *orchestrator.Result) error {
return fmt.Errorf("destroy not implemented")
}
type ExampleSO struct {
/* you can have some internal state here */
projectID string
handlers []orchestrator.ManifestHandler
}
func (so *ExampleSO) ProjectID() string {
return so.projectID
}
func (so *ExampleSO) Handlers() []orchestrator.ManifestHandler {
return so.handlers
}
func (so *ExampleSO) MiddlewareBefore(ctx context.Context, req orchestrator.Request, r *orchestrator.Result) error {
logger := logging.Ctx(ctx)
logger.Info().Msg("Before it begins")
if req.Origin.Repository.Visibility != orchestrator.RepositoryVisbilityPublic {
r.Fail("This sub-orchestrator only accepts manifests in public repositories")
return nil
}
if req.Sender.Type == orchestrator.SenderTypeUser {
logger.Info().Msg("#####")
client, err := oresources.NewIAMLookupClient(ctx, req.Resources.IAMLookup.URL)
if err != nil {
return err
}
access, err := client.GCPUserHasRoleInProjects(ctx, req.Sender.Email, "your_so_role", "ent-someproject-dev")
if err != nil {
return err
}
if !access {
r.Fail("You don't have access to ent-someproject-dev")
return nil
}
}
// The cache is shared between middlewares and handlers!
cache := orchestrator.Ctx(ctx)
cache.Set("cache_key", "something something!")
return nil
}
func (so ExampleSO) MiddlewareAfter(ctx context.Context, _ orchestrator.Request, res *orchestrator.Result) error {
logger := logging.Ctx(ctx)
logger.Info().Msg("Auditing this thing")
cache := orchestrator.Ctx(ctx)
value := cache.Get("cache_key")
if str, ok := value.(string); ok {
logger.Info().Msgf("Got value from cache: %s", str)
}
logger.Info().Msg("After it's done")
return nil
}
func NewExampleSO(projectID string) *ExampleSO {
return &ExampleSO{
projectID: projectID,
handlers: []orchestrator.ManifestHandler{
&ExampleManifestV1Handler{},
},
}
}
// -----------------------
// Complex Sub-Orchestrator Example
// -----------------------
func main() {
logger := logging.New(
logging.WithNoCaller(),
logging.WithLevel(logging.DebugLevel),
logging.WithWriter(
logging.NewConsoleWriter(
logging.WithNoColor(),
logging.WithNoTimestamp(),
),
),
)
iamServer, _ := oresources.NewMockIAMLookupServer(
oresources.WithPort(8001),
oresources.WithUserProjectRoles(
orchestrator.DefaultMockUserEmail,
"ent-someproject-dev",
[]string{"your_so_role"},
),
)
err := iamServer.Start()
if err != nil {
logger.Panic().Err(err).Send()
}
defer func() {
err := iamServer.Stop()
if err != nil {
logger.Panic().Err(err).Send()
}
}()
so := NewExampleSO("mysoproject")
handler := orchestrator.NewCloudEventHandler(so,
orchestrator.WithCustomLogger(logger),
orchestrator.WithCustomPubSubClient(nil),
)
manifest := ExampleManifestV1{
ManifestHeader: orchestrator.ManifestHeader{
APIVersion: so.handlers[0].APIVersion(),
Kind: so.handlers[0].Kind(),
},
Spec: ExampleSpecV1{
Name: "Test Name",
},
Metadata: ExampleMetadataV1{
ID: "manifestid",
},
}
e, _ := orchestrator.NewMockCloudEvent(manifest, orchestrator.WithIAMEndpoint(iamServer.URL()))
err = handler(context.Background(), *e)
if err != nil {
logger.Error().Err(err).Msg("Encountered error")
}
// The following output is expected and verified in tests:
}
Output: DBG Created a new CloudEventHandler DBG Processing request gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request={"action":"plan","apiVersion":"orchestrator.entur.io/request/v1","manifest":{"new":{"apiVersion":"orchestrator.entur.io/example/v1","kind":"Example","metadata":{"id":"manifestid"},"spec":{"name":"Test Name"}},"old":null},"metadata":{"contextId":"mockid","requestId":"mockid"},"origin":{"fileChanges":{"bloblUrl":"","contentsUrl":"","rawUrl":""},"fileName":"","pullRequest":{"body":"","htmlUrl":"","id":0,"labels":null,"number":0,"ref":"","state":"open","title":""},"repository":{"defaultBranch":"main","fullName":"entur/mockrepo","htmlUrl":"","id":0,"name":"mockrepo","visibility":"public"}},"resources":{"iamLookup":{"url":"http://localhost:8001"}},"responseTopic":"mocktopic","sender":{"githubEmail":"mockuser@entur.io","githubId":0,"githubLogin":"mockuser","githubRepositoryPermission":"admin","type":"user"}} gorch_request_id=mockid DBG Found ManifestHandler (orchestrator.entur.io/example/v1, Example) gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid DBG Executing Orchestrator MiddlewareBefore gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid INF Before it begins gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid INF ##### gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid DBG Unable to discover idtoken credentials, defaulting to http.Client for IAMLookup gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid DBG Executing ManifestHandler MiddlewareBefore (orchestrator.entur.io/example/v1, Example, plan) gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid INF After Orchestrator middleware executes, but before manifest handler executes gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid DBG Executing ManifestHandler (orchestrator.entur.io/example/v1, Example, plan) gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid DBG Executing Orchestrator MiddlewareAfter gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid INF Auditing this thing gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid INF Got value from cache: something something! gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid INF After it's done gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid WRN Pubsub client is set to null, no responses will be sent gorch_action=plan gorch_context_id=mockid gorch_file_name= gorch_github_user_id=0 gorch_request_id=mockid gorch_result_creations=[{}] gorch_result_deletions=[{}] gorch_result_summary="Plan all the things" gorch_result_updates=[{}]
Index ¶
- Constants
- func NewCloudEventHandler(so Orchestrator, opts ...HandlerOption) func(context.Context, cloudevent.Event) error
- func NewMockCloudEvent(manifest any, opts ...MockRequestOption) (*cloudevent.Event, error)
- func UnmarshalCloudEvent(e cloudevent.Event, v any) error
- type APIVersion
- type Action
- type Change
- type CloudEventData
- type ContextCache
- type FileChanges
- type HandlerConfig
- type HandlerOption
- type Kind
- type Manifest
- type ManifestHandler
- type ManifestHeader
- type ManifestMetadata
- type Manifests
- type MiddlewareAfter
- type MiddlewareBefore
- type MockRequestOption
- type Orchestrator
- type Origin
- type PubSubMessage
- type PubSubMessageAttributes
- type PullRequest
- type PullRequestState
- type Repository
- type RepositoryPermission
- type RepositoryVisibility
- type Request
- type RequestMetadata
- type Resource
- type ResourceIAMLookup
- type Resources
- type Response
- type Result
- func (r *Result) Code() ResultCode
- func (r *Result) Create(changes ...any)
- func (r *Result) Creations() []Change
- func (r *Result) Delete(changes ...any)
- func (r *Result) Deletions() []Change
- func (r *Result) Errors() []error
- func (r *Result) Fail(summary string)
- func (r *Result) Locked() bool
- func (r *Result) Output() string
- func (r *Result) Succeed(summary string)
- func (r *Result) Update(changes ...any)
- func (r *Result) Updates() []Change
- type ResultCode
- type Sender
- type SenderType
Examples ¶
Constants ¶
const DefaultMockAction = ActionPlan // Default User action used in PO request mocks.
const DefaultMockContextID = "mockid" // Default Context ID used in PO request mocks.
const DefaultMockDefaultBranch = "main" // Default Repository branch used in PO request mocks.
const DefaultMockPullRequestState = PullRequestStateOpen // Default Pull Request state used in PO request mocks.
const DefaultMockRepositoryFullName = "entur/mockrepo" // Default Repository full name used in PO request mocks.
const DefaultMockRepositoryName = "mockrepo" // Default Repository name used in PO request mocks.
const DefaultMockRepositoryVisibility = RepositoryVisbilityPublic // Default Repository visibility used in PO request mocks.
const DefaultMockRequestID = "mockid" // Default Request ID used in PO request mocks.
const DefaultMockResponseTopic = "mocktopic" // Default Topic ID used in PO request mocks.
const DefaultMockSenderType = SenderTypeUser // Default Repository branch used in PO request mocks.
const DefaultMockUserEmail = "mockuser@entur.io" // Default verified user email used in PO request mocks.
const DefaultMockUserPermission = RepositoryPermissionAdmin // Default Repository permissions used in PO request mocks.
const DefaultMockUsername = "mockuser" // Default Github username used in PO request mocks.
Variables ¶
This section is empty.
Functions ¶
func NewCloudEventHandler ¶ added in v1.5.0
func NewCloudEventHandler(so Orchestrator, opts ...HandlerOption) func(context.Context, cloudevent.Event) error
func NewMockCloudEvent ¶ added in v1.5.0
func NewMockCloudEvent(manifest any, opts ...MockRequestOption) (*cloudevent.Event, error)
func UnmarshalCloudEvent ¶ added in v1.5.0
func UnmarshalCloudEvent(e cloudevent.Event, v any) error
Types ¶
type APIVersion ¶ added in v1.5.0
type APIVersion string // Platform Orchestrator / Sub-Orchestrator APIVersion
const ( APIVersionOrchestratorResponseV1 APIVersion = "orchestrator.entur.io/request/v1" // Platform Orchestrator Request APIVersionOrchestratorRequestV1 APIVersion = "orchestrator.entur.io/response/v1" // Platform Orchestrator Response )
type Change ¶ added in v1.5.0
type Change interface {
String() string
}
The Change interface represents a planned/applied change in the context of a sub-orchestrator.
type CloudEventData ¶ added in v1.5.0
type CloudEventData struct {
Subscription string
Message PubSubMessage
}
type ContextCache ¶ added in v1.5.0
type ContextCache struct {
// contains filtered or unexported fields
}
func Ctx ¶ added in v1.5.0
func Ctx(ctx context.Context) ContextCache
Retrieve the value cache attached to the current request context
func (ContextCache) Get ¶ added in v1.5.0
func (c ContextCache) Get(key string) any
func (ContextCache) Set ¶ added in v1.5.0
func (c ContextCache) Set(key string, value any)
type FileChanges ¶ added in v1.5.0
type HandlerConfig ¶ added in v1.2.0
type HandlerConfig struct {
// contains filtered or unexported fields
}
type HandlerOption ¶ added in v1.2.0
type HandlerOption func(*HandlerConfig)
func WithCustomLogger ¶ added in v1.1.0
func WithCustomLogger(logger zerolog.Logger) HandlerOption
func WithCustomPubSubClient ¶ added in v1.5.0
func WithCustomPubSubClient(client *pubsub.Client) HandlerOption
type Manifest ¶
type Manifest = json.RawMessage
type ManifestHandler ¶
type ManifestHandler interface {
// Which APIVersion and Kind this handler operates on
APIVersion() APIVersion
Kind() Kind
// Actions
Plan(context.Context, Request, *Result) error
PlanDestroy(context.Context, Request, *Result) error
Apply(context.Context, Request, *Result) error
Destroy(context.Context, Request, *Result) error
}
The ManifestHandler interface represents the logic used for handling a specific APIVersion and Kind.
type ManifestHeader ¶
type ManifestHeader struct {
APIVersion APIVersion `json:"apiVersion" jsonschema:"required,minLength=1,maxLength=2083,pattern=^orchestrator\\.entur\\.io\\/.*\\/[vV].*$"` // 'orchestrator.entur.io/mysuborchestrator/v1'
Kind Kind `json:"kind" jsonschema:"required,minLength=2,maxLength=63"` // 'mymanifestkind'
}
type ManifestMetadata ¶ added in v1.7.0
type MiddlewareAfter ¶ added in v1.3.0
The MiddlewareAfter interface represents the middleware running after every manifest event and/or a specific handler.
type MiddlewareBefore ¶ added in v1.3.0
The MiddlewareBefore interface represents the middleware running before every manifest event and/or a specific handler.
type MockRequestOption ¶ added in v1.3.0
type MockRequestOption func(*Request)
func WithAction ¶ added in v1.5.0
func WithAction(action Action) MockRequestOption
func WithIAMEndpoint ¶ added in v1.5.0
func WithIAMEndpoint(url string) MockRequestOption
func WithSender ¶ added in v1.5.0
func WithSender(sender Sender) MockRequestOption
func WithVisibility ¶ added in v1.7.2
func WithVisibility(visbility RepositoryVisibility) MockRequestOption
type Orchestrator ¶
type Orchestrator interface {
Handlers() []ManifestHandler // The manifests this orchestrator can handle
}
The Orchestrator interface represents the main configuration of a sub-orchestrator in a Project.
type Origin ¶
type Origin struct {
FileName string `json:"fileName"`
Repository Repository `json:"repository"` // 'https://github.com/entur/some-repo'
FileChanges FileChanges `json:"fileChanges"`
PullRequest PullRequest `json:"pullRequest"`
}
type PubSubMessage ¶ added in v1.1.0
type PubSubMessage struct {
ID string `json:"messageId"`
PublishTime string `json:"publishTime"`
Attributes PubSubMessageAttributes `json:"attributes"`
Data []byte `json:"data"`
}
type PubSubMessageAttributes ¶ added in v1.1.0
type PubSubMessageAttributes struct{}
type PullRequest ¶ added in v1.5.0
type PullRequest struct {
ID int `json:"id"` // '123123145'
State PullRequestState `json:"state"` // 'open'
Ref string `json:"ref"`
Title string `json:"title"` // 'chore: Added .entur manifests'
Body string `json:"body"`
Number int `json:"number"`
Labels []string `json:"labels"`
HtmlURL string `json:"htmlUrl"`
}
type PullRequestState ¶ added in v1.5.0
type PullRequestState string
const ( PullRequestStateOpen PullRequestState = "open" PullRequestStateClosed PullRequestState = "closed" )
type Repository ¶ added in v1.5.0
type Repository struct {
ID int `json:"id"` // '123123145'
Name string `json:"name"` // 'some-remo'
FullName string `json:"fullName"` // 'entur/some-repo'
DefaultBranch string `json:"defaultBranch"` // 'main'
HtmlURL string `json:"htmlUrl"` // 'https://github.com/entur/some-repo'
Visibility RepositoryVisibility `json:"visibility"` // 'public'
}
type RepositoryPermission ¶ added in v1.5.0
type RepositoryPermission string
const ( RepositoryPermissionAdmin RepositoryPermission = "admin" RepositoryPermissionMaintain RepositoryPermission = "maintain" RepositoryPermissionWrite RepositoryPermission = "write" RepositoryPermissionTriage RepositoryPermission = "triage" RepositoryPermissionRead RepositoryPermission = "read" )
type RepositoryVisibility ¶ added in v1.5.0
type RepositoryVisibility string
const ( RepositoryVisbilityPublic RepositoryVisibility = "public" RepositoryVisbilityInternal RepositoryVisibility = "internal" RepositoryVisbilityPrivate RepositoryVisibility = "private" )
type Request ¶
type Request struct {
Action Action `json:"action"`
APIVersion APIVersion `json:"apiVersion"` // 'orchestrator.entur.io/request/v1'
Manifest Manifests `json:"manifest"`
Metadata RequestMetadata `json:"metadata"`
Resources Resources `json:"resources"`
Origin Origin `json:"origin"`
Sender Sender `json:"sender"`
ResponseTopic string `json:"responseTopic"`
}
func NewMockRequest ¶ added in v1.3.0
func NewMockRequest(manifest any, opts ...MockRequestOption) (*Request, error)
type RequestMetadata ¶ added in v1.7.0
type Resource ¶
type Resource struct {
URL string `json:"url"` // 'https://eu-west1.cloudfunctions.net/someresource'
}
type ResourceIAMLookup ¶
type ResourceIAMLookup = Resource
type Resources ¶
type Resources struct {
IAMLookup ResourceIAMLookup `json:"iamLookup"`
}
type Response ¶
type Response struct {
APIVersion APIVersion `json:"apiVersion"` // 'orchestrator.entur.io/response/v1'
Metadata RequestMetadata `json:"metadata"`
ResultCode ResultCode `json:"result"` // 'success'
Output string `json:"output"`
}
type Result ¶
type Result struct {
// contains filtered or unexported fields
}
func Process ¶ added in v1.1.0
func Process(ctx context.Context, so Orchestrator, req *Request) *Result
func (*Result) Create ¶
Add a new 'create' change to the result. Valid change types are: * string * Stringer/Change interface * Slices/Arrays containing Stringer/Change interfaces
func (*Result) Delete ¶
Add a new 'delete' change to the result. Valid change types are: * string * Stringer/Change interface * Slices/Arrays containing Stringer/Change interfaces
type ResultCode ¶
type ResultCode string
const ( ResultCodeSuccess ResultCode = "success" // Sub-Orchestrator succeeded in processing the action ResultCodeFailure ResultCode = "failure" // Sub-Orchestrator detected a user failure when processing the action ResultCodeNoop ResultCode = "noop" // Sub-Orchestrator detected no changes after processing the action ResultCodeError ResultCode = "error" // Sub-Orchestrator experienced an internal error when processing the action )
type Sender ¶
type Sender struct {
Username string `json:"githubLogin"` // 'mockuser'
Email string `json:"githubEmail"` // 'mockuser@entur.org'
ID int `json:"githubId"`
Permission RepositoryPermission `json:"githubRepositoryPermission"` // 'admin'
Type SenderType `json:"type"` // 'user'
}
type SenderType ¶
type SenderType string
const ( SenderTypeUser SenderType = "user" // Github user SenderTypeBot SenderType = "bot" // )