Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
# see https://entgo.io/docs/ci/
- uses: ent/contrib/ci@e38dfb6484dfbe64b8bd060fe6a219a1aa5da770 # master
name: "Check all ent generated code is checked in"
if: ${{ matrix.app != 'main-module' && matrix.app != 'cli' }}
if: ${{ matrix.app != 'main-module' }}
with:
working-directory: app/${{ matrix.app }}
tidy: true
Expand Down
2 changes: 1 addition & 1 deletion app/cli/cmd/attestation_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func newAttestationInitCmd() *cobra.Command {

cmd.Flags().StringVar(&projectName, "project", "", "name of the project of this workflow")
cobra.CheckErr(cmd.MarkFlagRequired("project"))
cmd.Flags().StringVar(&newWorkflowcontract, "contract", "", "name of an existing contract or the path/URL to a contract file to be used in the attestation. It will update any existing contract for the workflow")
cmd.Flags().StringVar(&newWorkflowcontract, "contract", "", "name of an existing contract or the path/URL to a contract file, to attach it to the auto-created workflow (it doesn't update an existing one)")

cmd.Flags().StringVar(&projectVersion, "version", "", "project version, i.e 0.1.0")
cmd.Flags().BoolVar(&projectVersionRelease, "release", false, "promote the provided version as a release")
Expand Down
125 changes: 34 additions & 91 deletions app/cli/internal/action/attestation_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"github.com/rs/zerolog"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/apimachinery/pkg/util/validation"
)

type AttestationInitOpts struct {
Expand Down Expand Up @@ -100,13 +99,38 @@
}

action.Logger.Debug().Msg("Retrieving attestation definition")
client := pb.NewAttestationServiceClient(action.CPConnection)
client := pb.NewAttestationServiceClient(action.ActionsOpts.CPConnection)

Check failure on line 102 in app/cli/internal/action/attestation_init.go

View workflow job for this annotation

GitHub Actions / lint (main-module)

QF1008: could remove embedded field "ActionsOpts" from selector (staticcheck)

// 1 - Create the workflow (and contract) if it doesn't exist
wf, err := action.createWorkflow(ctx, opts)
// 0 - find or create the contract if we are creating the workflow (if any)
contractRef := opts.NewWorkflowContractRef
_, err := NewWorkflowDescribe(action.ActionsOpts).Run(ctx, opts.WorkflowName, opts.ProjectName)
if err != nil && status.Code(err) == codes.NotFound {
// Not found, let's see if we need to create the contract
if contractRef != "" {
// Try to find it by name
_, err := NewWorkflowContractDescribe(action.ActionsOpts).Run(contractRef, 0)
// An invalid argument might be raised if we use a file or URL in the "name" field, which must be DNS-1123
// TODO: validate locally before doing the query
if err != nil && (status.Code(err) == codes.NotFound || status.Code(err) == codes.InvalidArgument) {
createResp, err := NewWorkflowContractCreate(action.ActionsOpts).Run(fmt.Sprintf("%s-%s", opts.ProjectName, opts.WorkflowName), nil, contractRef)
if err != nil {
return "", err
}
contractRef = createResp.Name
}
}
}

// 1 - Find or create the workflow
workflowsResp, err := client.FindOrCreateWorkflow(ctx, &pb.FindOrCreateWorkflowRequest{
ProjectName: opts.ProjectName,
WorkflowName: opts.WorkflowName,
ContractName: contractRef,
})
if err != nil {
return "", fmt.Errorf("error creating workflow: %w", err)
return "", err
}
workflow := workflowsResp.GetResult()

// 2 - Get contract
contractResp, err := client.GetContract(ctx, &pb.AttestationServiceGetContractRequest{
Expand All @@ -120,12 +144,12 @@

contractVersion := contractResp.Result.GetContract()
workflowMeta := &clientAPI.WorkflowMetadata{
WorkflowId: wf.ID,
Name: wf.Name,
Project: wf.Project,
Team: wf.Team,
WorkflowId: workflow.GetId(),
Name: workflow.GetName(),
Project: workflow.GetProject(),
Team: workflow.GetTeam(),
SchemaRevision: strconv.Itoa(int(contractVersion.GetRevision())),
ContractName: wf.ContractName,
ContractName: workflow.ContractName,
}

if opts.ProjectVersion != "" {
Expand Down Expand Up @@ -222,87 +246,6 @@
return attestationID, nil
}

// createWorkflow creates a new workflow if it doesn't exist, as well as its contract
func (action *AttestationInit) createWorkflow(ctx context.Context, opts *AttestationInitRunOpts) (*WorkflowItem, error) {
// 1. find workflow if exists
wf, err := NewWorkflowDescribe(action.ActionsOpts).Run(ctx, opts.WorkflowName, opts.ProjectName)
if err != nil && status.Code(err) != codes.NotFound {
return nil, fmt.Errorf("error looking for workflow %q: %w", opts.WorkflowName, err)
}

// if no new contract is provided, there's nothing to update
if wf != nil && opts.NewWorkflowContractRef == "" {
return wf, nil
}

contractRef := opts.NewWorkflowContractRef
contractName := contractRef
if contractRef != "" {
if isContractReference(contractRef) {
_, err := NewWorkflowContractDescribe(action.ActionsOpts).Run(contractRef, 0)
if err != nil {
return nil, fmt.Errorf("contract %q could not be found", contractRef)
}
// the contract exists
} else {
// use a default name for the contract
contractName = defaultContractName(opts.ProjectName, opts.WorkflowName)

// if the workflow exists, we just update its contract with the new contents
if wf != nil {
_, err = NewWorkflowContractUpdate(action.ActionsOpts).Run(wf.ContractName, nil, contractRef)
if err != nil {
return nil, fmt.Errorf("error updating contract %q: %w", wf.ContractName, err)
}
contractName = wf.ContractName
} else {
// if the workflow doesn't exist, let's create or update the contract
cont, err := NewWorkflowContractDescribe(action.ActionsOpts).Run(contractName, 0)
switch {
case err != nil && status.Code(err) == codes.NotFound:
// Contract not found, let's create it
_, err = NewWorkflowContractCreate(action.ActionsOpts).Run(contractName, nil, contractRef)
if err != nil {
return nil, err
}
case err != nil:
return nil, err
default:
// contract found, let's update it (chainloop will validate that there is an actual change in the contract file)
_, err := NewWorkflowContractUpdate(action.ActionsOpts).Run(cont.Contract.Name, &cont.Contract.Description, contractRef)
if err != nil {
return nil, err
}
}
}
}
}

// if workflow doesn't exist, let's create it with the contract
if wf == nil {
wf, err = NewWorkflowCreate(action.ActionsOpts).Run(&NewWorkflowCreateOpts{
Name: opts.WorkflowName,
Project: opts.ProjectName,
ContractName: contractName,
})
if err != nil {
return nil, fmt.Errorf("error creating workflow %q: %w", opts.WorkflowName, err)
}
}

return wf, nil
}

// isContractReference checks if the reference points to an existent contract name (DNS-1123)
func isContractReference(ref string) bool {
err := validation.IsDNS1123Label(ref)
return len(err) == 0
}

func defaultContractName(project, workflow string) string {
return fmt.Sprintf("%s-%s", project, workflow)
}

func enrichContractMaterials(ctx context.Context, schema *v1.CraftingSchema, client pb.AttestationServiceClient, logger *zerolog.Logger) error {
contractMaterials := schema.GetMaterials()
for _, pgAtt := range schema.GetPolicyGroups() {
Expand Down
Loading