diff --git a/constants/table.go b/constants/table.go index 07d81d7e..1841f452 100644 --- a/constants/table.go +++ b/constants/table.go @@ -9,6 +9,9 @@ const ( // TableBuild defines the table type for the database builds table. TableBuild = "builds" + // TableBuildExecutable defines the table type for the database build_executables table. + TableBuildExecutable = "build_executables" + // TableHook defines the table type for the database hooks table. TableHook = "hooks" diff --git a/database/build_executable.go b/database/build_executable.go new file mode 100644 index 00000000..bbec42f5 --- /dev/null +++ b/database/build_executable.go @@ -0,0 +1,167 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package database + +import ( + "database/sql" + "encoding/base64" + "errors" + + "github.com/go-vela/types/library" +) + +var ( + // ErrEmptyBuildExecutableBuildID defines the error type when a + // BuildExecutable type has an empty BuildID field provided. + ErrEmptyBuildExecutableBuildID = errors.New("empty build_executable build_id provided") +) + +// BuildExecutable is the database representation of a BuildExecutable. +type BuildExecutable struct { + ID sql.NullInt64 `sql:"id"` + BuildID sql.NullInt64 `sql:"build_id"` + Data []byte `sql:"data"` +} + +// Compress will manipulate the existing data for the +// BuildExecutable by compressing that data. This produces +// a significantly smaller amount of data that is +// stored in the system. +func (b *BuildExecutable) Compress(level int) error { + // compress the database BuildExecutable data + data, err := compress(level, b.Data) + if err != nil { + return err + } + + // overwrite database BuildExecutable data with compressed BuildExecutable data + b.Data = data + + return nil +} + +// Decompress will manipulate the existing data for the +// BuildExecutable by decompressing that data. This allows us +// to have a significantly smaller amount of data that +// is stored in the system. +func (b *BuildExecutable) Decompress() error { + // decompress the database BuildExecutable data + data, err := decompress(b.Data) + if err != nil { + return err + } + + // overwrite compressed BuildExecutable data with decompressed BuildExecutable data + b.Data = data + + return nil +} + +// Decrypt will manipulate the existing executable data by +// base64 decoding that value. Then, a AES-256 cipher +// block is created from the encryption key in order to +// decrypt the base64 decoded secret value. +func (b *BuildExecutable) Decrypt(key string) error { + dst := make([]byte, base64.StdEncoding.DecodedLen(len(b.Data))) + + // base64 decode the encrypted repo hash + n, err := base64.StdEncoding.Decode(dst, b.Data) + if err != nil { + return err + } + + dst = dst[:n] + + // decrypt the base64 decoded executable data + decrypted, err := decrypt(key, dst) + if err != nil { + return err + } + + // set the decrypted executable + b.Data = decrypted + + return nil +} + +// Encrypt will manipulate the existing build executable by +// creating a AES-256 cipher block from the encryption +// key in order to encrypt the build executable. Then, the +// build executable is base64 encoded for transport across +// network boundaries. +func (b *BuildExecutable) Encrypt(key string) error { + // encrypt the executable data + encrypted, err := encrypt(key, b.Data) + if err != nil { + return err + } + + // base64 encode the encrypted executable to make it network safe + dst := make([]byte, base64.StdEncoding.EncodedLen(len(encrypted))) + base64.StdEncoding.Encode(dst, encrypted) + + b.Data = dst + + return nil +} + +// Nullify ensures the valid flag for +// the sql.Null types are properly set. +// +// When a field within the BuildExecutable type is the zero +// value for the field, the valid flag is set to +// false causing it to be NULL in the database. +func (b *BuildExecutable) Nullify() *BuildExecutable { + if b == nil { + return nil + } + + // check if the ID field should be false + if b.ID.Int64 == 0 { + b.ID.Valid = false + } + + // check if the BuildID field should be false + if b.BuildID.Int64 == 0 { + b.BuildID.Valid = false + } + + return b +} + +// ToLibrary converts the BuildExecutable type +// to a library BuildExecutable type. +func (b *BuildExecutable) ToLibrary() *library.BuildExecutable { + buildExecutable := new(library.BuildExecutable) + + buildExecutable.SetID(b.ID.Int64) + buildExecutable.SetBuildID(b.BuildID.Int64) + buildExecutable.SetData(b.Data) + + return buildExecutable +} + +// Validate verifies the necessary fields for +// the BuildExecutable type are populated correctly. +func (b *BuildExecutable) Validate() error { + // verify the BuildID field is populated + if b.BuildID.Int64 <= 0 { + return ErrEmptyBuildExecutableBuildID + } + + return nil +} + +// BuildExecutableFromLibrary converts the library BuildExecutable type +// to a database BuildExecutable type. +func BuildExecutableFromLibrary(c *library.BuildExecutable) *BuildExecutable { + buildExecutable := &BuildExecutable{ + ID: sql.NullInt64{Int64: c.GetID(), Valid: true}, + BuildID: sql.NullInt64{Int64: c.GetBuildID(), Valid: true}, + Data: c.GetData(), + } + + return buildExecutable.Nullify() +} diff --git a/database/build_executable_test.go b/database/build_executable_test.go new file mode 100644 index 00000000..9ccac72f --- /dev/null +++ b/database/build_executable_test.go @@ -0,0 +1,457 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package database + +import ( + "database/sql" + "reflect" + "testing" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/library" +) + +func TestDatabase_BuildExecutable_Compress(t *testing.T) { + // setup tests + tests := []struct { + name string + failure bool + level int + buildExecutable *BuildExecutable + want []byte + }{ + { + name: "compression level -1", + failure: false, + level: constants.CompressionNegOne, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 156, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 0", + failure: false, + level: constants.CompressionZero, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 1, 0, 108, 1, 147, 254, 10, 123, 32, 10, 32, 32, 32, 32, 34, 105, 100, 34, 58, 32, 34, 115, 116, 101, 112, 95, 110, 97, 109, 101, 34, 44, 10, 32, 32, 32, 32, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 32, 34, 49, 34, 44, 10, 32, 32, 32, 32, 34, 109, 101, 116, 97, 100, 97, 116, 97, 34, 58, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 99, 108, 111, 110, 101, 34, 58, 116, 114, 117, 101, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 101, 110, 118, 105, 114, 111, 110, 109, 101, 110, 116, 34, 58, 91, 34, 115, 116, 101, 112, 115, 34, 44, 34, 115, 101, 114, 118, 105, 99, 101, 115, 34, 44, 34, 115, 101, 99, 114, 101, 116, 115, 34, 93, 125, 44, 10, 32, 32, 32, 32, 34, 119, 111, 114, 107, 101, 114, 34, 58, 123, 125, 44, 10, 32, 32, 32, 32, 34, 115, 116, 101, 112, 115, 34, 58, 91, 10, 32, 32, 32, 32, 32, 32, 32, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 105, 100, 34, 58, 34, 115, 116, 101, 112, 95, 103, 105, 116, 104, 117, 98, 95, 111, 99, 116, 111, 99, 97, 116, 95, 49, 95, 105, 110, 105, 116, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 100, 105, 114, 101, 99, 116, 111, 114, 121, 34, 58, 34, 47, 118, 101, 108, 97, 47, 115, 114, 99, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 103, 105, 116, 104, 117, 98, 47, 111, 99, 116, 111, 99, 97, 116, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 101, 110, 118, 105, 114, 111, 110, 109, 101, 110, 116, 34, 58, 32, 123, 34, 66, 85, 73, 76, 68, 95, 65, 85, 84, 72, 79, 82, 34, 58, 34, 79, 99, 116, 111, 99, 97, 116, 34, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 93, 10, 125, 10, 1, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 1", + failure: false, + level: constants.CompressionOne, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 1, 92, 143, 65, 75, 3, 49, 16, 133, 239, 249, 21, 195, 59, 23, 151, 94, 115, 83, 60, 40, 8, 5, 177, 167, 82, 66, 76, 7, 13, 118, 19, 153, 76, 87, 100, 217, 255, 46, 217, 236, 86, 244, 157, 134, 151, 121, 243, 190, 152, 145, 12, 17, 17, 226, 9, 150, 80, 148, 63, 93, 242, 61, 99, 211, 236, 129, 165, 196, 156, 234, 219, 118, 245, 122, 86, 127, 242, 234, 97, 199, 121, 169, 10, 225, 156, 19, 195, 170, 92, 184, 69, 171, 192, 105, 136, 146, 83, 207, 73, 97, 15, 243, 249, 130, 13, 10, 203, 16, 3, 183, 49, 8, 107, 193, 113, 106, 49, 124, 101, 249, 96, 129, 29, 87, 163, 50, 21, 216, 195, 181, 235, 183, 181, 106, 38, 159, 151, 220, 91, 212, 247, 203, 171, 203, 65, 115, 240, 234, 182, 46, 166, 168, 11, 245, 42, 156, 162, 112, 208, 44, 223, 176, 232, 6, 62, 251, 174, 72, 232, 90, 244, 38, 228, 126, 25, 187, 229, 202, 255, 248, 159, 31, 209, 136, 187, 253, 227, 211, 189, 187, 221, 191, 60, 236, 158, 97, 177, 107, 221, 152, 174, 180, 147, 33, 34, 58, 154, 201, 252, 4, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 2", + failure: false, + level: constants.CompressionTwo, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 94, 92, 143, 65, 75, 195, 64, 16, 133, 239, 249, 21, 203, 59, 23, 67, 175, 123, 83, 60, 40, 8, 5, 177, 167, 82, 150, 117, 51, 232, 98, 179, 35, 179, 211, 136, 132, 252, 119, 49, 147, 70, 236, 237, 241, 146, 111, 222, 183, 205, 232, 26, 231, 156, 67, 238, 224, 29, 170, 210, 103, 40, 177, 39, 108, 172, 30, 72, 106, 230, 242, 251, 109, 123, 233, 122, 210, 216, 69, 141, 240, 227, 252, 211, 204, 167, 19, 23, 130, 87, 57, 147, 161, 115, 75, 101, 200, 194, 165, 167, 162, 240, 135, 249, 124, 197, 6, 149, 100, 200, 137, 44, 38, 33, 173, 56, 78, 203, 226, 23, 203, 7, 9, 252, 120, 41, 12, 242, 135, 117, 235, 111, 117, 53, 55, 241, 183, 172, 239, 231, 215, 192, 73, 57, 69, 13, 219, 144, 75, 214, 197, 122, 21, 237, 178, 80, 82, 150, 111, 120, 180, 3, 157, 98, 91, 37, 181, 134, 222, 36, 238, 151, 216, 46, 87, 174, 241, 127, 47, 114, 35, 238, 246, 143, 79, 247, 225, 118, 255, 242, 176, 123, 134, 199, 206, 182, 49, 173, 182, 150, 142, 205, 212, 252, 4, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 3", + failure: false, + level: constants.CompressionThree, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 94, 92, 143, 65, 75, 195, 64, 16, 133, 239, 249, 21, 203, 59, 23, 67, 175, 123, 83, 60, 40, 8, 5, 177, 167, 82, 150, 117, 51, 232, 98, 179, 35, 179, 211, 136, 132, 252, 119, 49, 147, 70, 236, 237, 241, 118, 222, 123, 223, 54, 163, 107, 156, 115, 14, 185, 131, 119, 168, 74, 159, 161, 196, 158, 176, 49, 123, 32, 169, 153, 203, 239, 219, 246, 226, 245, 164, 177, 139, 26, 225, 199, 249, 104, 62, 76, 39, 46, 4, 175, 114, 38, 139, 206, 46, 149, 33, 11, 151, 158, 138, 194, 31, 230, 250, 138, 13, 42, 201, 144, 19, 153, 76, 66, 90, 113, 156, 150, 197, 47, 150, 15, 18, 248, 241, 98, 88, 200, 31, 214, 214, 191, 213, 149, 220, 192, 223, 178, 190, 159, 95, 3, 39, 229, 20, 53, 108, 67, 46, 89, 23, 234, 21, 169, 203, 66, 73, 89, 190, 225, 209, 14, 116, 138, 109, 149, 212, 90, 244, 38, 113, 191, 200, 118, 105, 185, 142, 255, 251, 145, 27, 113, 183, 127, 124, 186, 15, 183, 251, 151, 135, 221, 51, 60, 118, 182, 141, 105, 165, 53, 117, 108, 166, 230, 39, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 4", + failure: false, + level: constants.CompressionFour, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 94, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 23, 151, 94, 115, 83, 60, 40, 8, 5, 177, 167, 82, 66, 204, 14, 26, 236, 102, 100, 50, 93, 145, 101, 255, 187, 52, 73, 87, 244, 54, 124, 51, 223, 203, 75, 55, 155, 206, 24, 99, 16, 7, 88, 131, 172, 244, 233, 146, 31, 9, 155, 138, 39, 146, 28, 57, 93, 118, 219, 43, 27, 73, 253, 224, 213, 195, 206, 5, 20, 24, 78, 156, 8, 86, 229, 76, 245, 172, 80, 74, 83, 20, 78, 35, 37, 133, 61, 148, 248, 140, 13, 50, 201, 20, 3, 213, 49, 8, 105, 198, 113, 105, 233, 95, 44, 31, 36, 176, 243, 21, 84, 201, 30, 214, 212, 223, 87, 203, 254, 210, 188, 22, 127, 139, 250, 126, 126, 117, 28, 148, 131, 87, 183, 117, 49, 69, 109, 173, 215, 243, 33, 10, 5, 101, 249, 134, 69, 63, 209, 201, 247, 89, 66, 95, 213, 155, 192, 99, 27, 251, 150, 242, 95, 255, 243, 35, 51, 227, 110, 255, 248, 116, 239, 110, 247, 47, 15, 187, 103, 88, 236, 154, 181, 172, 86, 157, 142, 221, 210, 253, 4, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 5", + failure: false, + level: constants.CompressionFive, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 94, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 6", + failure: false, + level: constants.CompressionSix, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 156, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 7", + failure: false, + level: constants.CompressionSeven, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 218, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 8", + failure: false, + level: constants.CompressionEight, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 218, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + { + name: "compression level 9", + failure: false, + level: constants.CompressionNine, + buildExecutable: &BuildExecutable{Data: testBuildExecutableData()}, + want: []byte{120, 218, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.buildExecutable.Compress(test.level) + + if test.failure { + if err == nil { + t.Errorf("Compress for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("Compress for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(test.buildExecutable.Data, test.want) { + t.Errorf("Compress for %s is %v, want %v", test.name, test.buildExecutable.Data, string(test.want)) + } + }) + } +} + +func TestDatabase_BuildExecutable_Decompress(t *testing.T) { + // setup tests + tests := []struct { + name string + failure bool + buildExecutable *BuildExecutable + want []byte + }{ + { + name: "compression level -1", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 156, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 0", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 1, 0, 108, 1, 147, 254, 10, 123, 32, 10, 32, 32, 32, 32, 34, 105, 100, 34, 58, 32, 34, 115, 116, 101, 112, 95, 110, 97, 109, 101, 34, 44, 10, 32, 32, 32, 32, 34, 118, 101, 114, 115, 105, 111, 110, 34, 58, 32, 34, 49, 34, 44, 10, 32, 32, 32, 32, 34, 109, 101, 116, 97, 100, 97, 116, 97, 34, 58, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 99, 108, 111, 110, 101, 34, 58, 116, 114, 117, 101, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 34, 101, 110, 118, 105, 114, 111, 110, 109, 101, 110, 116, 34, 58, 91, 34, 115, 116, 101, 112, 115, 34, 44, 34, 115, 101, 114, 118, 105, 99, 101, 115, 34, 44, 34, 115, 101, 99, 114, 101, 116, 115, 34, 93, 125, 44, 10, 32, 32, 32, 32, 34, 119, 111, 114, 107, 101, 114, 34, 58, 123, 125, 44, 10, 32, 32, 32, 32, 34, 115, 116, 101, 112, 115, 34, 58, 91, 10, 32, 32, 32, 32, 32, 32, 32, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 105, 100, 34, 58, 34, 115, 116, 101, 112, 95, 103, 105, 116, 104, 117, 98, 95, 111, 99, 116, 111, 99, 97, 116, 95, 49, 95, 105, 110, 105, 116, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 100, 105, 114, 101, 99, 116, 111, 114, 121, 34, 58, 34, 47, 118, 101, 108, 97, 47, 115, 114, 99, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 103, 105, 116, 104, 117, 98, 47, 111, 99, 116, 111, 99, 97, 116, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 101, 110, 118, 105, 114, 111, 110, 109, 101, 110, 116, 34, 58, 32, 123, 34, 66, 85, 73, 76, 68, 95, 65, 85, 84, 72, 79, 82, 34, 58, 34, 79, 99, 116, 111, 99, 97, 116, 34, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 93, 10, 125, 10, 1, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 1", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 1, 92, 143, 65, 75, 3, 49, 16, 133, 239, 249, 21, 195, 59, 23, 151, 94, 115, 83, 60, 40, 8, 5, 177, 167, 82, 66, 76, 7, 13, 118, 19, 153, 76, 87, 100, 217, 255, 46, 217, 236, 86, 244, 157, 134, 151, 121, 243, 190, 152, 145, 12, 17, 17, 226, 9, 150, 80, 148, 63, 93, 242, 61, 99, 211, 236, 129, 165, 196, 156, 234, 219, 118, 245, 122, 86, 127, 242, 234, 97, 199, 121, 169, 10, 225, 156, 19, 195, 170, 92, 184, 69, 171, 192, 105, 136, 146, 83, 207, 73, 97, 15, 243, 249, 130, 13, 10, 203, 16, 3, 183, 49, 8, 107, 193, 113, 106, 49, 124, 101, 249, 96, 129, 29, 87, 163, 50, 21, 216, 195, 181, 235, 183, 181, 106, 38, 159, 151, 220, 91, 212, 247, 203, 171, 203, 65, 115, 240, 234, 182, 46, 166, 168, 11, 245, 42, 156, 162, 112, 208, 44, 223, 176, 232, 6, 62, 251, 174, 72, 232, 90, 244, 38, 228, 126, 25, 187, 229, 202, 255, 248, 159, 31, 209, 136, 187, 253, 227, 211, 189, 187, 221, 191, 60, 236, 158, 97, 177, 107, 221, 152, 174, 180, 147, 33, 34, 58, 154, 201, 252, 4, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 2", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 94, 92, 143, 65, 75, 195, 64, 16, 133, 239, 249, 21, 203, 59, 23, 67, 175, 123, 83, 60, 40, 8, 5, 177, 167, 82, 150, 117, 51, 232, 98, 179, 35, 179, 211, 136, 132, 252, 119, 49, 147, 70, 236, 237, 241, 146, 111, 222, 183, 205, 232, 26, 231, 156, 67, 238, 224, 29, 170, 210, 103, 40, 177, 39, 108, 172, 30, 72, 106, 230, 242, 251, 109, 123, 233, 122, 210, 216, 69, 141, 240, 227, 252, 211, 204, 167, 19, 23, 130, 87, 57, 147, 161, 115, 75, 101, 200, 194, 165, 167, 162, 240, 135, 249, 124, 197, 6, 149, 100, 200, 137, 44, 38, 33, 173, 56, 78, 203, 226, 23, 203, 7, 9, 252, 120, 41, 12, 242, 135, 117, 235, 111, 117, 53, 55, 241, 183, 172, 239, 231, 215, 192, 73, 57, 69, 13, 219, 144, 75, 214, 197, 122, 21, 237, 178, 80, 82, 150, 111, 120, 180, 3, 157, 98, 91, 37, 181, 134, 222, 36, 238, 151, 216, 46, 87, 174, 241, 127, 47, 114, 35, 238, 246, 143, 79, 247, 225, 118, 255, 242, 176, 123, 134, 199, 206, 182, 49, 173, 182, 150, 142, 205, 212, 252, 4, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 3", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 94, 92, 143, 65, 75, 195, 64, 16, 133, 239, 249, 21, 203, 59, 23, 67, 175, 123, 83, 60, 40, 8, 5, 177, 167, 82, 150, 117, 51, 232, 98, 179, 35, 179, 211, 136, 132, 252, 119, 49, 147, 70, 236, 237, 241, 118, 222, 123, 223, 54, 163, 107, 156, 115, 14, 185, 131, 119, 168, 74, 159, 161, 196, 158, 176, 49, 123, 32, 169, 153, 203, 239, 219, 246, 226, 245, 164, 177, 139, 26, 225, 199, 249, 104, 62, 76, 39, 46, 4, 175, 114, 38, 139, 206, 46, 149, 33, 11, 151, 158, 138, 194, 31, 230, 250, 138, 13, 42, 201, 144, 19, 153, 76, 66, 90, 113, 156, 150, 197, 47, 150, 15, 18, 248, 241, 98, 88, 200, 31, 214, 214, 191, 213, 149, 220, 192, 223, 178, 190, 159, 95, 3, 39, 229, 20, 53, 108, 67, 46, 89, 23, 234, 21, 169, 203, 66, 73, 89, 190, 225, 209, 14, 116, 138, 109, 149, 212, 90, 244, 38, 113, 191, 200, 118, 105, 185, 142, 255, 251, 145, 27, 113, 183, 127, 124, 186, 15, 183, 251, 151, 135, 221, 51, 60, 118, 182, 141, 105, 165, 53, 117, 108, 166, 230, 39, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 4", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 94, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 23, 151, 94, 115, 83, 60, 40, 8, 5, 177, 167, 82, 66, 204, 14, 26, 236, 102, 100, 50, 93, 145, 101, 255, 187, 52, 73, 87, 244, 54, 124, 51, 223, 203, 75, 55, 155, 206, 24, 99, 16, 7, 88, 131, 172, 244, 233, 146, 31, 9, 155, 138, 39, 146, 28, 57, 93, 118, 219, 43, 27, 73, 253, 224, 213, 195, 206, 5, 20, 24, 78, 156, 8, 86, 229, 76, 245, 172, 80, 74, 83, 20, 78, 35, 37, 133, 61, 148, 248, 140, 13, 50, 201, 20, 3, 213, 49, 8, 105, 198, 113, 105, 233, 95, 44, 31, 36, 176, 243, 21, 84, 201, 30, 214, 212, 223, 87, 203, 254, 210, 188, 22, 127, 139, 250, 126, 126, 117, 28, 148, 131, 87, 183, 117, 49, 69, 109, 173, 215, 243, 33, 10, 5, 101, 249, 134, 69, 63, 209, 201, 247, 89, 66, 95, 213, 155, 192, 99, 27, 251, 150, 242, 95, 255, 243, 35, 51, 227, 110, 255, 248, 116, 239, 110, 247, 47, 15, 187, 103, 88, 236, 154, 181, 172, 86, 157, 142, 221, 210, 253, 4, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 5", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 94, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 6", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 156, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 7", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 218, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 8", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 218, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + { + name: "compression level 9", + failure: false, + buildExecutable: &BuildExecutable{Data: []byte{120, 218, 92, 143, 65, 75, 3, 49, 16, 133, 239, 251, 43, 194, 59, 47, 46, 189, 230, 166, 120, 80, 16, 10, 98, 79, 165, 132, 152, 29, 52, 216, 205, 200, 100, 186, 34, 203, 254, 119, 113, 147, 110, 105, 111, 143, 47, 249, 94, 94, 154, 201, 52, 198, 24, 131, 216, 195, 26, 100, 165, 111, 151, 252, 64, 104, 11, 30, 73, 114, 228, 244, 127, 182, 57, 179, 129, 212, 247, 94, 61, 236, 180, 128, 5, 134, 35, 39, 130, 85, 57, 81, 123, 161, 148, 198, 40, 156, 6, 74, 10, 187, 95, 234, 51, 90, 100, 146, 49, 6, 42, 49, 8, 105, 198, 97, 174, 237, 63, 44, 95, 36, 176, 211, 25, 20, 201, 238, 215, 214, 203, 171, 235, 242, 50, 252, 35, 234, 231, 233, 221, 113, 80, 14, 94, 221, 198, 197, 20, 21, 237, 245, 245, 62, 10, 5, 101, 249, 133, 69, 55, 210, 209, 119, 89, 66, 87, 212, 187, 192, 67, 141, 93, 109, 185, 213, 175, 126, 100, 38, 60, 236, 158, 95, 30, 221, 253, 238, 237, 105, 251, 10, 139, 109, 181, 230, 213, 42, 233, 208, 204, 205, 95, 0, 0, 0, 255, 255, 225, 62, 100, 105}}, + want: testBuildExecutableData(), + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.buildExecutable.Decompress() + + if test.failure { + if err == nil { + t.Errorf("Decompress for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("Decompress for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(test.buildExecutable.Data, test.want) { + t.Errorf("Decompress for %s is %v, want %v", test.name, string(test.buildExecutable.Data), string(test.want)) + } + }) + } +} + +func TestDatabase_BuildExecutable_Decrypt(t *testing.T) { + // setup types + key := "C639A572E14D5075C526FDDD43E4ECF6" + encrypted := testBuildExecutable() + + err := encrypted.Encrypt(key) + if err != nil { + t.Errorf("unable to encrypt repo: %v", err) + } + + // setup tests + tests := []struct { + failure bool + key string + executable BuildExecutable + }{ + { + failure: false, + key: key, + executable: *encrypted, + }, + { + failure: true, + key: "", + executable: *encrypted, + }, + { + failure: true, + key: key, + executable: *testBuildExecutable(), + }, + } + + // run tests + for _, test := range tests { + err := test.executable.Decrypt(test.key) + + if test.failure { + if err == nil { + t.Errorf("Decrypt should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("Decrypt returned err: %v", err) + } + } +} + +func TestDatabase_BuildExecutable_Encrypt(t *testing.T) { + // setup types + key := "C639A572E14D5075C526FDDD43E4ECF6" + + // setup tests + tests := []struct { + failure bool + key string + executable *BuildExecutable + }{ + { + failure: false, + key: key, + executable: testBuildExecutable(), + }, + { + failure: true, + key: "", + executable: testBuildExecutable(), + }, + } + + // run tests + for _, test := range tests { + err := test.executable.Encrypt(test.key) + + if test.failure { + if err == nil { + t.Errorf("Encrypt should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("Encrypt returned err: %v", err) + } + } +} + +func TestDatabase_BuildExecutable_Nullify(t *testing.T) { + // setup types + var p *BuildExecutable + + want := &BuildExecutable{ + ID: sql.NullInt64{Int64: 0, Valid: false}, + BuildID: sql.NullInt64{Int64: 0, Valid: false}, + } + + // setup tests + tests := []struct { + buildExecutable *BuildExecutable + want *BuildExecutable + }{ + { + buildExecutable: testBuildExecutable(), + want: testBuildExecutable(), + }, + { + buildExecutable: p, + want: nil, + }, + { + buildExecutable: new(BuildExecutable), + want: want, + }, + } + + // run tests + for _, test := range tests { + got := test.buildExecutable.Nullify() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("Nullify is %v, want %v", got, test.want) + } + } +} + +func TestDatabase_BuildExecutable_ToLibrary(t *testing.T) { + // setup types + want := new(library.BuildExecutable) + + want.SetID(1) + want.SetBuildID(1) + want.SetData(testBuildExecutableData()) + + // run test + got := testBuildExecutable().ToLibrary() + + if !reflect.DeepEqual(got, want) { + t.Errorf("ToLibrary is %v, want %v", got, want) + } +} + +func TestDatabase_BuildExecutable_Validate(t *testing.T) { + // setup tests + tests := []struct { + failure bool + buildExecutable *BuildExecutable + }{ + { + failure: false, + buildExecutable: testBuildExecutable(), + }, + { // no build_id set for buildExecutable + failure: true, + buildExecutable: &BuildExecutable{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + }, + }, + } + + // run tests + for _, test := range tests { + err := test.buildExecutable.Validate() + + if test.failure { + if err == nil { + t.Errorf("Validate should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("Validate returned err: %v", err) + } + } +} + +func TestDatabase_BuildExecutableFromLibrary(t *testing.T) { + // setup types + c := new(library.BuildExecutable) + + c.SetID(1) + c.SetBuildID(1) + c.SetData(testBuildExecutableData()) + + want := testBuildExecutable() + + // run test + got := BuildExecutableFromLibrary(c) + + if !reflect.DeepEqual(got, want) { + t.Errorf("BuildExecutableFromLibrary is %v, want %v", got, want) + } +} + +// testBuildExecutable is a test helper function to create a BuildExecutable +// type with all fields set to a fake value. +func testBuildExecutable() *BuildExecutable { + return &BuildExecutable{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + BuildID: sql.NullInt64{Int64: 1, Valid: true}, + Data: testBuildExecutableData(), + } +} + +// testBuildExecutableData is a test helper function to create the +// content for the Data field for the BuildExecutable type. +func testBuildExecutableData() []byte { + return []byte(` +{ + "id": "step_name", + "version": "1", + "metadata":{ + "clone":true, + "environment":["steps","services","secrets"]}, + "worker":{}, + "steps":[ + { + "id":"step_github_octocat_1_init", + "directory":"/vela/src/github.com/github/octocat", + "environment": {"BUILD_AUTHOR":"Octocat"} + } + ] +} +`) +} diff --git a/database/schedule.go b/database/schedule.go index 2000f4f8..44c880ab 100644 --- a/database/schedule.go +++ b/database/schedule.go @@ -38,6 +38,7 @@ type Schedule struct { UpdatedAt sql.NullInt64 `sql:"updated_at"` UpdatedBy sql.NullString `sql:"updated_by"` ScheduledAt sql.NullInt64 `sql:"scheduled_at"` + Branch sql.NullString `sql:"branch"` } // ScheduleFromLibrary converts the library.Schedule type to a database Schedule type. @@ -53,6 +54,7 @@ func ScheduleFromLibrary(s *library.Schedule) *Schedule { UpdatedAt: sql.NullInt64{Int64: s.GetUpdatedAt(), Valid: true}, UpdatedBy: sql.NullString{String: s.GetUpdatedBy(), Valid: true}, ScheduledAt: sql.NullInt64{Int64: s.GetScheduledAt(), Valid: true}, + Branch: sql.NullString{String: s.GetBranch(), Valid: true}, } return schedule.Nullify() @@ -87,6 +89,8 @@ func (s *Schedule) Nullify() *Schedule { s.UpdatedBy.Valid = len(s.UpdatedBy.String) != 0 // check if the ScheduledAt field should be valid s.ScheduledAt.Valid = s.ScheduledAt.Int64 != 0 + // check if the Branch field should be valid + s.Branch.Valid = len(s.Branch.String) != 0 return s } @@ -104,6 +108,7 @@ func (s *Schedule) ToLibrary() *library.Schedule { UpdatedAt: &s.UpdatedAt.Int64, UpdatedBy: &s.UpdatedBy.String, ScheduledAt: &s.ScheduledAt.Int64, + Branch: &s.Branch.String, } } diff --git a/database/schedule_test.go b/database/schedule_test.go index ee1a06dd..904960ba 100644 --- a/database/schedule_test.go +++ b/database/schedule_test.go @@ -25,6 +25,7 @@ func TestDatabase_ScheduleFromLibrary(t *testing.T) { s.SetUpdatedAt(time.Now().Add(time.Hour * 1).UTC().Unix()) s.SetUpdatedBy("user2") s.SetScheduledAt(time.Now().Add(time.Hour * 2).UTC().Unix()) + s.SetBranch("main") want := testSchedule() @@ -59,6 +60,7 @@ func TestDatabase_Schedule_Nullify(t *testing.T) { UpdatedAt: sql.NullInt64{Int64: 0, Valid: false}, UpdatedBy: sql.NullString{String: "", Valid: false}, ScheduledAt: sql.NullInt64{Int64: 0, Valid: false}, + Branch: sql.NullString{String: "", Valid: false}, }, }, { @@ -90,6 +92,7 @@ func TestDatabase_Schedule_ToLibrary(t *testing.T) { want.SetUpdatedAt(time.Now().Add(time.Hour * 1).UTC().Unix()) want.SetUpdatedBy("user2") want.SetScheduledAt(time.Now().Add(time.Hour * 2).UTC().Unix()) + want.SetBranch("main") got := testSchedule().ToLibrary() if !reflect.DeepEqual(got, want) { @@ -176,5 +179,6 @@ func testSchedule() *Schedule { UpdatedAt: sql.NullInt64{Int64: time.Now().Add(time.Hour * 1).UTC().Unix(), Valid: true}, UpdatedBy: sql.NullString{String: "user2", Valid: true}, ScheduledAt: sql.NullInt64{Int64: time.Now().Add(time.Hour * 2).UTC().Unix(), Valid: true}, + Branch: sql.NullString{String: "main", Valid: true}, } } diff --git a/go.mod b/go.mod index b390f929..6d469162 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,19 @@ module github.com/go-vela/types go 1.19 require ( - github.com/adhocore/gronx v1.6.3 + github.com/adhocore/gronx v1.6.5 github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 github.com/drone/envsubst v1.0.3 github.com/ghodss/yaml v1.0.0 github.com/lib/pq v1.10.9 - github.com/microcosm-cc/bluemonday v1.0.24 + github.com/microcosm-cc/bluemonday v1.0.25 ) require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/kr/pretty v0.2.0 // indirect - golang.org/x/net v0.10.0 // indirect + golang.org/x/net v0.12.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index 1aaf12af..f5135c4e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/adhocore/gronx v1.6.3 h1:bnm5vieTrY3QQPpsfB0hrAaeaHDpuZTUC2LLCVMLe9c= -github.com/adhocore/gronx v1.6.3/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= +github.com/adhocore/gronx v1.6.5 h1:/pryEagBKz3WqUgpgvtL51eBN2rJLXowuW7rpS+jrew= +github.com/adhocore/gronx v1.6.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/buildkite/yaml v0.0.0-20181016232759-0caa5f0796e3 h1:q+sMKdA6L8LyGVudTkpGoC73h6ak2iWSPFiFo/pFOU8= @@ -19,10 +19,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw= -github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/item.go b/item.go index 972d4c97..ca68b0b0 100644 --- a/item.go +++ b/item.go @@ -6,29 +6,26 @@ package types import ( "github.com/go-vela/types/library" - "github.com/go-vela/types/pipeline" ) // ItemVersion allows the worker to detect items that were queued before an Vela server // upgrade or downgrade, so it can handle such stale data gracefully. // For example, the worker could fail a stale build or ask the server to recompile it. // This is not a public API and is unrelated to the version key in pipeline yaml. -const ItemVersion uint64 = 1 +const ItemVersion uint64 = 2 // Item is the queue representation of an item to publish to the queue. type Item struct { - Build *library.Build `json:"build"` - Pipeline *pipeline.Build `json:"pipeline"` - Repo *library.Repo `json:"repo"` - User *library.User `json:"user"` + Build *library.Build `json:"build"` + Repo *library.Repo `json:"repo"` + User *library.User `json:"user"` // The 0-value is the implicit ItemVersion for queued Items that pre-date adding the field. ItemVersion uint64 `json:"item_version"` } -// ToItem creates a queue item from a pipeline, build, repo and user. -func ToItem(p *pipeline.Build, b *library.Build, r *library.Repo, u *library.User) *Item { +// ToItem creates a queue item from a build, repo and user. +func ToItem(b *library.Build, r *library.Repo, u *library.User) *Item { return &Item{ - Pipeline: p, Build: b, Repo: r, User: u, diff --git a/item_test.go b/item_test.go index b451b707..901c83d8 100644 --- a/item_test.go +++ b/item_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/go-vela/types/library" - "github.com/go-vela/types/pipeline" ) func TestTypes_ToItem(t *testing.T) { @@ -44,26 +43,6 @@ func TestTypes_ToItem(t *testing.T) { Ref: &str, BaseRef: &str, } - p := &pipeline.Build{ - Version: "v1", - Stages: pipeline.StageSlice{ - &pipeline.Stage{ - Name: str, - Steps: pipeline.ContainerSlice{ - &pipeline.Container{ - Image: "alpine", - Name: str, - }, - }, - }, - }, - Steps: pipeline.ContainerSlice{ - &pipeline.Container{ - Image: "alpine", - Name: str, - }, - }, - } r := &library.Repo{ ID: &num64, UserID: &num64, @@ -88,26 +67,6 @@ func TestTypes_ToItem(t *testing.T) { Admin: &booL, } want := &Item{ - Pipeline: &pipeline.Build{ - Version: "v1", - Stages: pipeline.StageSlice{ - &pipeline.Stage{ - Name: str, - Steps: pipeline.ContainerSlice{ - &pipeline.Container{ - Image: "alpine", - Name: str, - }, - }, - }, - }, - Steps: pipeline.ContainerSlice{ - &pipeline.Container{ - Image: "alpine", - Name: str, - }, - }, - }, Build: &library.Build{ ID: &num64, RepoID: &num64, @@ -159,7 +118,7 @@ func TestTypes_ToItem(t *testing.T) { } // run test - got := ToItem(p, b, r, u) + got := ToItem(b, r, u) if !reflect.DeepEqual(got, want) { t.Errorf("ToItem is %v, want %v", got, want) diff --git a/library/build_executable.go b/library/build_executable.go new file mode 100644 index 00000000..dedc8e05 --- /dev/null +++ b/library/build_executable.go @@ -0,0 +1,110 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package library + +import ( + "fmt" +) + +// BuildExecutable is the library representation of a BuildExecutable. +// +// swagger:model BuildExecutable +type BuildExecutable struct { + ID *int64 `json:"id,omitempty"` + BuildID *int64 `json:"build_id,omitempty"` + // swagger:strfmt base64 + Data *[]byte `json:"data,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided BuildExecutable type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *BuildExecutable) GetID() int64 { + // return zero value if BuildExecutable type or ID field is nil + if b == nil || b.ID == nil { + return 0 + } + + return *b.ID +} + +// GetBuildID returns the BuildID field. +// +// When the provided BuildExecutable type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *BuildExecutable) GetBuildID() int64 { + // return zero value if BuildExecutable type or BuildID field is nil + if b == nil || b.BuildID == nil { + return 0 + } + + return *b.BuildID +} + +// GetData returns the Data field. +// +// When the provided BuildExecutable type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (b *BuildExecutable) GetData() []byte { + // return zero value if BuildExecutable type or Data field is nil + if b == nil || b.Data == nil { + return []byte{} + } + + return *b.Data +} + +// SetID sets the ID field. +// +// When the provided BuildExecutable type is nil, it +// will set nothing and immediately return. +func (b *BuildExecutable) SetID(v int64) { + // return if BuildExecutable type is nil + if b == nil { + return + } + + b.ID = &v +} + +// SetBuildID sets the BuildID field. +// +// When the provided BuildExecutable type is nil, it +// will set nothing and immediately return. +func (b *BuildExecutable) SetBuildID(v int64) { + // return if BuildExecutable type is nil + if b == nil { + return + } + + b.BuildID = &v +} + +// SetData sets the Data field. +// +// When the provided BuildExecutable type is nil, it +// will set nothing and immediately return. +func (b *BuildExecutable) SetData(v []byte) { + // return if Log type is nil + if b == nil { + return + } + + b.Data = &v +} + +// String implements the Stringer interface for the BuildExecutable type. +func (b *BuildExecutable) String() string { + return fmt.Sprintf(`{ + ID: %d, + BuildID: %d, + Data: %s, +}`, + b.GetID(), + b.GetBuildID(), + b.GetData(), + ) +} diff --git a/library/build_executable_test.go b/library/build_executable_test.go new file mode 100644 index 00000000..e02c011a --- /dev/null +++ b/library/build_executable_test.go @@ -0,0 +1,138 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package library + +import ( + "fmt" + "reflect" + "testing" +) + +func TestLibrary_BuildExecutable_Getters(t *testing.T) { + // setup tests + tests := []struct { + buildExecutable *BuildExecutable + want *BuildExecutable + }{ + { + buildExecutable: testBuildExecutable(), + want: testBuildExecutable(), + }, + { + buildExecutable: new(BuildExecutable), + want: new(BuildExecutable), + }, + } + + // run tests + for _, test := range tests { + if test.buildExecutable.GetID() != test.want.GetID() { + t.Errorf("GetID is %v, want %v", test.buildExecutable.GetID(), test.want.GetID()) + } + + if test.buildExecutable.GetBuildID() != test.want.GetBuildID() { + t.Errorf("GetBuildID is %v, want %v", test.buildExecutable.GetBuildID(), test.want.GetBuildID()) + } + + if !reflect.DeepEqual(test.buildExecutable.GetData(), test.want.GetData()) { + t.Errorf("GetData is %v, want %v", test.buildExecutable.GetData(), test.want.GetData()) + } + } +} + +func TestLibrary_BuildExecutable_Setters(t *testing.T) { + // setup types + var bExecutable *BuildExecutable + + // setup tests + tests := []struct { + buildExecutable *BuildExecutable + want *BuildExecutable + }{ + { + buildExecutable: testBuildExecutable(), + want: testBuildExecutable(), + }, + { + buildExecutable: bExecutable, + want: new(BuildExecutable), + }, + } + + // run tests + for _, test := range tests { + test.buildExecutable.SetID(test.want.GetID()) + test.buildExecutable.SetBuildID(test.want.GetBuildID()) + test.buildExecutable.SetData(test.want.GetData()) + + if test.buildExecutable.GetID() != test.want.GetID() { + t.Errorf("SetID is %v, want %v", test.buildExecutable.GetID(), test.want.GetID()) + } + + if test.buildExecutable.GetBuildID() != test.want.GetBuildID() { + t.Errorf("SetRepoID is %v, want %v", test.buildExecutable.GetBuildID(), test.want.GetBuildID()) + } + + if !reflect.DeepEqual(test.buildExecutable.GetData(), test.want.GetData()) { + t.Errorf("SetData is %v, want %v", test.buildExecutable.GetData(), test.want.GetData()) + } + } +} + +func TestLibrary_BuildExecutable_String(t *testing.T) { + // setup types + bExecutable := testBuildExecutable() + + want := fmt.Sprintf(`{ + ID: %d, + BuildID: %d, + Data: %s, +}`, + bExecutable.GetID(), + bExecutable.GetBuildID(), + bExecutable.GetData(), + ) + + // run test + got := bExecutable.String() + + if !reflect.DeepEqual(got, want) { + t.Errorf("String is %v, want %v", got, want) + } +} + +// testBuildExecutable is a test helper function to create a Pipeline +// type with all fields set to a fake value. +func testBuildExecutable() *BuildExecutable { + p := new(BuildExecutable) + + p.SetID(1) + p.SetBuildID(1) + p.SetData(testBuildExecutableData()) + + return p +} + +// testBuildExecutableData is a test helper function to create the +// content for the Data field for the Pipeline type. +func testBuildExecutableData() []byte { + return []byte(` +{ + "id": "step_name", + "version": "1", + "metadata":{ + "clone":true, + "environment":["steps","services","secrets"]}, + "worker":{}, + "steps":[ + { + "id":"step_github_octocat_1_init", + "directory":"/vela/src/github.com/github/octocat", + "environment": {"BUILD_AUTHOR":"Octocat"} + } + ] +} +`) +} diff --git a/library/log.go b/library/log.go index e38de1ed..a3966bf3 100644 --- a/library/log.go +++ b/library/log.go @@ -56,7 +56,8 @@ func (l *Log) MaskData(secrets []string) { // create regexp to match secrets in the log data surrounded by regexp metacharacters // // https://pkg.go.dev/regexp#MustCompile - re := regexp.MustCompile((`(\s|^|=|"|:|'|\.|,)` + escaped + `(\s|$|"|:|'|\.|,)`)) + buffer := `(\s|^|=|"|\?|:|'|\.|,|&|$|;)` + re := regexp.MustCompile((buffer + escaped + buffer)) // create a mask for the secret mask := fmt.Sprintf("$1%s$2", constants.SecretLogMask) diff --git a/library/log_test.go b/library/log_test.go index c25cf6f3..30e8e783 100644 --- a/library/log_test.go +++ b/library/log_test.go @@ -54,6 +54,8 @@ func TestLibrary_Log_MaskData(t *testing.T) { s3Masked := "$ echo $SECRET1\n***\n$ echo $SECRET2\n***\n" s4 := "SOME_SECRET=((%.YY245***pP.><@@}}" s4Masked := "SOME_SECRET=***" + s5 := "www.example.com?username=secret&password=extrasecret" + s5Masked := "www.example.com?username=***&password=***" tests := []struct { want []byte @@ -80,6 +82,11 @@ func TestLibrary_Log_MaskData(t *testing.T) { log: []byte(s4), secrets: sVals, }, + { // secret baked in URL query params + want: []byte(s5Masked), + log: []byte(s5), + secrets: sVals, + }, { // empty secrets slice want: []byte(s3), log: []byte(s3), diff --git a/library/schedule.go b/library/schedule.go index 2f74c5f9..947c2f45 100644 --- a/library/schedule.go +++ b/library/schedule.go @@ -22,6 +22,7 @@ type Schedule struct { UpdatedAt *int64 `json:"updated_at,omitempty"` UpdatedBy *string `json:"updated_by,omitempty"` ScheduledAt *int64 `json:"scheduled_at,omitempty"` + Branch *string `json:"branch,omitempty"` } // GetID returns the ID field from the provided Schedule. If the object is nil, @@ -134,6 +135,17 @@ func (s *Schedule) GetScheduledAt() int64 { return *s.ScheduledAt } +// GetBranch returns the Branch field from the provided Schedule. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (s *Schedule) GetBranch() string { + // return zero value if Schedule type or ScheduledAt field is nil + if s == nil || s.Branch == nil { + return "" + } + + return *s.Branch +} + // SetID sets the ID field in the provided Schedule. If the object is nil, // it will set nothing and immediately return making this a no-op. func (s *Schedule) SetID(id int64) { @@ -244,6 +256,17 @@ func (s *Schedule) SetScheduledAt(scheduledAt int64) { s.ScheduledAt = &scheduledAt } +// SetBranch sets the Branch field in the provided Schedule. If the object is nil, +// it will set nothing and immediately return making this a no-op. +func (s *Schedule) SetBranch(branch string) { + // return if Schedule type is nil + if s == nil { + return + } + + s.Branch = &branch +} + // String implements the Stringer interface for the Schedule type. func (s *Schedule) String() string { return fmt.Sprintf(`{ @@ -257,6 +280,7 @@ func (s *Schedule) String() string { ScheduledAt: %d, UpdatedAt: %d, UpdatedBy: %s, + Branch: %s, }`, s.GetActive(), s.GetCreatedAt(), @@ -268,5 +292,6 @@ func (s *Schedule) String() string { s.GetScheduledAt(), s.GetUpdatedAt(), s.GetUpdatedBy(), + s.GetBranch(), ) } diff --git a/library/schedule_test.go b/library/schedule_test.go index 9cf75313..bf953e4b 100644 --- a/library/schedule_test.go +++ b/library/schedule_test.go @@ -66,6 +66,9 @@ func TestLibrary_Schedule_Getters(t *testing.T) { if test.schedule.GetScheduledAt() != test.want.GetScheduledAt() { t.Errorf("GetScheduledAt is %v, want %v", test.schedule.GetScheduledAt(), test.want.GetScheduledAt()) } + if test.schedule.GetBranch() != test.want.GetBranch() { + t.Errorf("GetBranch is %v, want %v", test.schedule.GetBranch(), test.want.GetBranch()) + } }) } } @@ -136,6 +139,10 @@ func TestLibrary_Schedule_Setters(t *testing.T) { if test.schedule.GetScheduledAt() != test.want.GetScheduledAt() { t.Errorf("SetScheduledAt is %v, want %v", test.schedule.GetScheduledAt(), test.want.GetScheduledAt()) } + test.schedule.SetBranch(test.want.GetBranch()) + if test.schedule.GetBranch() != test.want.GetBranch() { + t.Errorf("SetBranch is %v, want %v", test.schedule.GetBranch(), test.want.GetBranch()) + } }) } } @@ -154,6 +161,7 @@ func TestLibrary_Schedule_String(t *testing.T) { ScheduledAt: %d, UpdatedAt: %d, UpdatedBy: %s, + Branch: %s, }`, s.GetActive(), s.GetCreatedAt(), @@ -165,6 +173,7 @@ func TestLibrary_Schedule_String(t *testing.T) { s.GetScheduledAt(), s.GetUpdatedAt(), s.GetUpdatedBy(), + s.GetBranch(), ) got := s.String() @@ -186,6 +195,7 @@ func testSchedule() *Schedule { s.SetUpdatedAt(time.Now().Add(time.Hour * 1).UTC().Unix()) s.SetUpdatedBy("user2") s.SetScheduledAt(time.Now().Add(time.Hour * 2).UTC().Unix()) + s.SetBranch("main") return s } diff --git a/library/secret.go b/library/secret.go index e4d498f3..3d7846ba 100644 --- a/library/secret.go +++ b/library/secret.go @@ -70,7 +70,7 @@ func (s *Secret) Match(from *pipeline.Container) bool { } // check incoming events - switch from.Environment["BUILD_EVENT"] { + switch from.Environment["VELA_BUILD_EVENT"] { case constants.EventPush: eACL = checkEvent(events, constants.EventPush) case constants.EventPull: @@ -81,6 +81,8 @@ func (s *Secret) Match(from *pipeline.Container) bool { eACL = checkEvent(events, constants.EventDeploy) case constants.EventComment: eACL = checkEvent(events, constants.EventComment) + case constants.EventSchedule: + eACL = checkEvent(events, constants.EventSchedule) } // check images whitelist diff --git a/library/secret_test.go b/library/secret_test.go index c97b18a9..069ebe12 100644 --- a/library/secret_test.go +++ b/library/secret_test.go @@ -43,7 +43,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { // test matching secret events step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push"}}, want: true, @@ -51,7 +51,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "pull_request"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "pull_request"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"pull_request"}}, want: true, @@ -59,7 +59,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "tag"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "tag"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"tag"}}, want: true, @@ -67,7 +67,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "deployment"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "deployment"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"deployment"}}, want: true, @@ -75,7 +75,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "comment"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "comment"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"comment"}}, want: true, @@ -83,7 +83,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "fake_event"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "fake_event"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push"}}, want: false, @@ -91,16 +91,24 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push", "pull_request"}}, want: true, }, + { + step: &pipeline.Container{ + Image: "alpine:latest", + Environment: map[string]string{"VELA_BUILD_EVENT": "schedule"}, + }, + sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push", "pull_request", "schedule"}}, + want: true, + }, { // test matching secret images step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine"}, Events: &[]string{}}, want: true, @@ -108,7 +116,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:latest"}, Events: &[]string{}}, want: true, @@ -116,7 +124,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:1"}, Events: &[]string{}}, want: false, @@ -124,7 +132,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine", "centos"}, Events: &[]string{}}, want: true, @@ -133,7 +141,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { // test matching secret events and images step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine"}, Events: &[]string{"push"}}, want: true, @@ -141,7 +149,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:latest"}, Events: &[]string{"push"}}, want: true, @@ -149,7 +157,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:1"}, Events: &[]string{"push"}}, want: false, @@ -157,7 +165,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "pull_request"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "pull_request"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:latest"}, Events: &[]string{"push"}}, want: false, @@ -165,7 +173,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine", "centos"}, Events: &[]string{"push"}}, want: true, @@ -174,7 +182,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { // test build events with image ACLs and rulesets step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, Ruleset: pipeline.Ruleset{ If: pipeline.Rules{ Event: []string{"push"}, @@ -187,7 +195,7 @@ func TestLibrary_Secret_Match(t *testing.T) { { step: &pipeline.Container{ Image: "alpine:latest", - Environment: map[string]string{"BUILD_EVENT": "push"}, + Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, Ruleset: pipeline.Ruleset{ If: pipeline.Rules{ Event: []string{"push"}, diff --git a/library/worker_registration.go b/library/worker_registration.go new file mode 100644 index 00000000..4c319829 --- /dev/null +++ b/library/worker_registration.go @@ -0,0 +1,92 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package library + +// WorkerRegistration is the library representation of a WorkerRegistration. +// +// swagger:model WorkerRegistration +type WorkerRegistration struct { + RegistrationToken *string `json:"registration_token,omitempty"` + QueuePublicKey *string `json:"queue_public_key,omitempty"` + QueueAddress *string `json:"queue_address,omitempty"` +} + +// GetRegistrationToken returns the RegistrationToken field. +// +// When the provided WorkerRegistration type is nil, or the field within +// the type is nil, it returns an empty string for the field. +func (w *WorkerRegistration) GetRegistrationToken() string { + // return zero value if WorkerRegistration type or RegistrationToken field is nil + if w == nil || w.RegistrationToken == nil { + return "" + } + + return *w.RegistrationToken +} + +// GetPublicKey returns the QueuePublicKey field. +// +// When the provided WorkerRegistration type is nil, or the field within +// the type is nil, it returns an empty string for the field. +func (w *WorkerRegistration) GetPublicKey() string { + // return zero value if WorkerRegistration type or QueuePublicKey field is nil + if w == nil || w.QueuePublicKey == nil { + return "" + } + + return *w.QueuePublicKey +} + +// GetQueueAddress returns the QueueAddress field. +// +// When the provided WorkerRegistration type is nil, or the field within +// the type is nil, it returns an empty string for the field. +func (w *WorkerRegistration) GetQueueAddress() string { + // return zero value if WorkerRegistration type or QueueAddress field is nil + if w == nil || w.QueueAddress == nil { + return "" + } + + return *w.QueueAddress +} + +// SetRegistrationToken sets the RegistrationToken field. +// +// When the provided WorkerRegistration type is nil, it +// will set nothing and immediately return. +func (w *WorkerRegistration) SetRegistrationToken(v string) { + // return if WorkerRegistration type is nil + if w == nil { + return + } + + w.RegistrationToken = &v +} + +// SetPublicKey sets the QueuePublicKey field. +// +// When the provided WorkerRegistration type is nil, it +// will set nothing and immediately return. +func (w *WorkerRegistration) SetPublicKey(v string) { + // return if WorkerRegistration type is nil + if w == nil { + return + } + + w.QueuePublicKey = &v +} + +// SetQueueAddress sets the QueueAddress field. +// +// When the provided WorkerRegistration type is nil, it +// will set nothing and immediately return. +func (w *WorkerRegistration) SetQueueAddress(v string) { + // return if WorkerRegistration type is nil + if w == nil { + return + } + + w.QueueAddress = &v +} diff --git a/library/worker_registration_test.go b/library/worker_registration_test.go new file mode 100644 index 00000000..26a520c1 --- /dev/null +++ b/library/worker_registration_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package library + +import ( + "testing" +) + +func TestLibrary_WorkerRegistration_Getters(t *testing.T) { + // setup tests + tests := []struct { + wR *WorkerRegistration + want *WorkerRegistration + }{ + { + wR: testWorkerRegistration(), + want: testWorkerRegistration(), + }, + { + wR: new(WorkerRegistration), + want: new(WorkerRegistration), + }, + } + + // run tests + for _, test := range tests { + if test.wR.GetRegistrationToken() != test.want.GetRegistrationToken() { + t.Errorf("GetRegistrationToken is %v, want %v", test.wR.GetRegistrationToken(), test.want.GetRegistrationToken()) + } + + if test.wR.GetQueueAddress() != test.want.GetQueueAddress() { + t.Errorf("GetQueueAddress is %v, want %v", test.wR.GetQueueAddress(), test.want.GetQueueAddress()) + } + + if test.wR.GetPublicKey() != test.want.GetPublicKey() { + t.Errorf("GetPublicKey is %v, want %v", test.wR.GetPublicKey(), test.want.GetPublicKey()) + } + } +} + +func TestLibrary_WorkerRegistration_Setters(t *testing.T) { + // setup types + var w *WorkerRegistration + + // setup tests + tests := []struct { + wR *WorkerRegistration + want *WorkerRegistration + }{ + { + wR: testWorkerRegistration(), + want: testWorkerRegistration(), + }, + { + wR: w, + want: new(WorkerRegistration), + }, + } + + // run tests + for _, test := range tests { + test.wR.SetRegistrationToken(test.want.GetRegistrationToken()) + test.wR.SetQueueAddress(test.want.GetQueueAddress()) + test.wR.SetPublicKey(test.want.GetPublicKey()) + + if test.wR.GetRegistrationToken() != test.want.GetRegistrationToken() { + t.Errorf("GetRegistrationToken is %v, want %v", test.wR.GetRegistrationToken(), test.want.GetRegistrationToken()) + } + + if test.wR.GetQueueAddress() != test.want.GetQueueAddress() { + t.Errorf("GetQueueAddress is %v, want %v", test.wR.GetQueueAddress(), test.want.GetQueueAddress()) + } + + if test.wR.GetPublicKey() != test.want.GetPublicKey() { + t.Errorf("GetPublicKey is %v, want %v", test.wR.GetPublicKey(), test.want.GetPublicKey()) + } + } +} + +// testWorkerRegistration is a test helper function to register a WorkerRegistration +// type with all fields set to a fake value. +func testWorkerRegistration() *WorkerRegistration { + w := new(WorkerRegistration) + w.SetRegistrationToken("1234356") + w.SetQueueAddress("http://localhost:8080") + w.SetPublicKey("isfnw1234") + + return w +}