-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add support for chisel release format "v1" #115
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package setup | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
|
||
"golang.org/x/crypto/openpgp/packet" | ||
"gopkg.in/yaml.v3" | ||
|
||
"github.com/canonical/chisel/internal/pgputil" | ||
) | ||
|
||
func parseReleaseChiselV1(baseDir, filePath string, data []byte) (*Release, error) { | ||
type yamlArchive struct { | ||
Version string `yaml:"version"` | ||
Suites []string `yaml:"suites"` | ||
Components []string `yaml:"components"` | ||
Default bool `yaml:"default"` | ||
PubKeys []string `yaml:"v1-public-keys"` | ||
} | ||
type yamlPubKey struct { | ||
ID string `yaml:"id"` | ||
Armor string `yaml:"armor"` | ||
} | ||
type yamlRelease struct { | ||
Format string `yaml:"format"` | ||
Archives map[string]yamlArchive `yaml:"archives"` | ||
PubKeys map[string]yamlPubKey `yaml:"v1-public-keys"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't feel like we need all of this logic when the only thing changing is the name of this field. How about adding the two fields to the same struct, and if the format is the old one, just put it in the new field? Everything else could be the same, I guess? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had the same discussion with Rafid on Friday, which is why I added a comment to try to explain it. I see your point about the code being much sorter that way (even with some extra logic for error reporting). However, I think it couples both format parsers when they should be independent, for example if we have to change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will make your proposed changes if you disagree, just wanted to make sure I got the rationale across correctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few points:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see your points, but I still think the flexibility and separation of concerns is still worth it. Especially if there are unforeseen changes that make it necessary to not drop "chisel-v1" in a few weeks. That said, I disagree and commit, I have made the changes to the PR. |
||
} | ||
const yamlReleaseFormat = "chisel-v1" | ||
|
||
release := &Release{ | ||
Path: baseDir, | ||
Packages: make(map[string]*Package), | ||
Archives: make(map[string]*Archive), | ||
} | ||
|
||
fileName := stripBase(baseDir, filePath) | ||
|
||
yamlVar := yamlRelease{} | ||
dec := yaml.NewDecoder(bytes.NewBuffer(data)) | ||
dec.KnownFields(false) | ||
err := dec.Decode(&yamlVar) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: cannot parse release definition: %v", fileName, err) | ||
} | ||
if yamlVar.Format != yamlReleaseFormat { | ||
return nil, fmt.Errorf("%s: expected format %q, got %q", fileName, yamlReleaseFormat, yamlVar.Format) | ||
} | ||
if len(yamlVar.Archives) == 0 { | ||
return nil, fmt.Errorf("%s: no archives defined", fileName) | ||
} | ||
|
||
// Decode the public keys and match against provided IDs. | ||
pubKeys := make(map[string]*packet.PublicKey, len(yamlVar.PubKeys)) | ||
for keyName, yamlPubKey := range yamlVar.PubKeys { | ||
key, err := pgputil.DecodePubKey([]byte(yamlPubKey.Armor)) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: cannot decode public key %q: %w", fileName, keyName, err) | ||
} | ||
if yamlPubKey.ID != key.KeyIdString() { | ||
return nil, fmt.Errorf("%s: public key %q armor has incorrect ID: expected %q, got %q", fileName, keyName, yamlPubKey.ID, key.KeyIdString()) | ||
} | ||
pubKeys[keyName] = key | ||
} | ||
|
||
for archiveName, details := range yamlVar.Archives { | ||
if details.Version == "" { | ||
return nil, fmt.Errorf("%s: archive %q missing version field", fileName, archiveName) | ||
} | ||
if len(details.Suites) == 0 { | ||
adjective := ubuntuAdjectives[details.Version] | ||
if adjective == "" { | ||
return nil, fmt.Errorf("%s: archive %q missing suites field", fileName, archiveName) | ||
} | ||
details.Suites = []string{adjective} | ||
} | ||
if len(details.Components) == 0 { | ||
return nil, fmt.Errorf("%s: archive %q missing components field", fileName, archiveName) | ||
} | ||
if len(yamlVar.Archives) == 1 { | ||
details.Default = true | ||
} else if details.Default && release.DefaultArchive != "" { | ||
return nil, fmt.Errorf("%s: more than one default archive: %s, %s", fileName, release.DefaultArchive, archiveName) | ||
} | ||
if details.Default { | ||
release.DefaultArchive = archiveName | ||
} | ||
if len(details.PubKeys) == 0 { | ||
return nil, fmt.Errorf("%s: archive %q missing v1-public-keys field", fileName, archiveName) | ||
} | ||
var archiveKeys []*packet.PublicKey | ||
for _, keyName := range details.PubKeys { | ||
key, ok := pubKeys[keyName] | ||
if !ok { | ||
return nil, fmt.Errorf("%s: archive %q refers to undefined public key %q", fileName, archiveName, keyName) | ||
} | ||
archiveKeys = append(archiveKeys, key) | ||
} | ||
release.Archives[archiveName] = &Archive{ | ||
Name: archiveName, | ||
Version: details.Version, | ||
Suites: details.Suites, | ||
Components: details.Components, | ||
PubKeys: archiveKeys, | ||
} | ||
} | ||
|
||
return release, err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package setup | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
|
||
"golang.org/x/crypto/openpgp/packet" | ||
"gopkg.in/yaml.v3" | ||
|
||
"github.com/canonical/chisel/internal/pgputil" | ||
) | ||
|
||
func parseReleaseV1(baseDir, filePath string, data []byte) (*Release, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [For reviewer]: Diff against chisel-v1 https://www.diffchecker.com/nYQFzN77/ . |
||
type yamlArchive struct { | ||
Version string `yaml:"version"` | ||
Suites []string `yaml:"suites"` | ||
Components []string `yaml:"components"` | ||
Default bool `yaml:"default"` | ||
PubKeys []string `yaml:"public-keys"` | ||
} | ||
type yamlPubKey struct { | ||
ID string `yaml:"id"` | ||
Armor string `yaml:"armor"` | ||
} | ||
type yamlRelease struct { | ||
Format string `yaml:"format"` | ||
Archives map[string]yamlArchive `yaml:"archives"` | ||
PubKeys map[string]yamlPubKey `yaml:"public-keys"` | ||
} | ||
const yamlReleaseFormat = "v1" | ||
|
||
release := &Release{ | ||
Path: baseDir, | ||
Packages: make(map[string]*Package), | ||
Archives: make(map[string]*Archive), | ||
} | ||
|
||
fileName := stripBase(baseDir, filePath) | ||
|
||
yamlVar := yamlRelease{} | ||
dec := yaml.NewDecoder(bytes.NewBuffer(data)) | ||
dec.KnownFields(false) | ||
err := dec.Decode(&yamlVar) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: cannot parse release definition: %v", fileName, err) | ||
} | ||
if yamlVar.Format != yamlReleaseFormat { | ||
return nil, fmt.Errorf("%s: expected format %q, got %q", fileName, yamlReleaseFormat, yamlVar.Format) | ||
} | ||
if len(yamlVar.Archives) == 0 { | ||
return nil, fmt.Errorf("%s: no archives defined", fileName) | ||
} | ||
|
||
// Decode the public keys and match against provided IDs. | ||
pubKeys := make(map[string]*packet.PublicKey, len(yamlVar.PubKeys)) | ||
for keyName, yamlPubKey := range yamlVar.PubKeys { | ||
key, err := pgputil.DecodePubKey([]byte(yamlPubKey.Armor)) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: cannot decode public key %q: %w", fileName, keyName, err) | ||
} | ||
if yamlPubKey.ID != key.KeyIdString() { | ||
return nil, fmt.Errorf("%s: public key %q armor has incorrect ID: expected %q, got %q", fileName, keyName, yamlPubKey.ID, key.KeyIdString()) | ||
} | ||
pubKeys[keyName] = key | ||
} | ||
|
||
for archiveName, details := range yamlVar.Archives { | ||
if details.Version == "" { | ||
return nil, fmt.Errorf("%s: archive %q missing version field", fileName, archiveName) | ||
} | ||
if len(details.Suites) == 0 { | ||
adjective := ubuntuAdjectives[details.Version] | ||
if adjective == "" { | ||
return nil, fmt.Errorf("%s: archive %q missing suites field", fileName, archiveName) | ||
} | ||
details.Suites = []string{adjective} | ||
} | ||
if len(details.Components) == 0 { | ||
return nil, fmt.Errorf("%s: archive %q missing components field", fileName, archiveName) | ||
} | ||
if len(yamlVar.Archives) == 1 { | ||
details.Default = true | ||
} else if details.Default && release.DefaultArchive != "" { | ||
return nil, fmt.Errorf("%s: more than one default archive: %s, %s", fileName, release.DefaultArchive, archiveName) | ||
} | ||
if details.Default { | ||
release.DefaultArchive = archiveName | ||
} | ||
if len(details.PubKeys) == 0 { | ||
return nil, fmt.Errorf("%s: archive %q missing public-keys field", fileName, archiveName) | ||
} | ||
var archiveKeys []*packet.PublicKey | ||
for _, keyName := range details.PubKeys { | ||
key, ok := pubKeys[keyName] | ||
if !ok { | ||
return nil, fmt.Errorf("%s: archive %q refers to undefined public key %q", fileName, archiveName, keyName) | ||
} | ||
archiveKeys = append(archiveKeys, key) | ||
} | ||
release.Archives[archiveName] = &Archive{ | ||
Name: archiveName, | ||
Version: details.Version, | ||
Suites: details.Suites, | ||
Components: details.Components, | ||
PubKeys: archiveKeys, | ||
} | ||
} | ||
|
||
return release, err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[For reviewer]: The code was moved from
setup.go
and the only change was moving the types inside of the function to prevent name clashes withv1
. Proof: https://www.diffchecker.com/zg6qjOmM/ .