Skip to content

Commit

Permalink
Merge pull request #69 from atc0005/promote-v1-to-stable-format
Browse files Browse the repository at this point in the history
Promote v1 to stable format
  • Loading branch information
atc0005 authored Dec 3, 2024
2 parents 7f8bfe3 + 6e62a24 commit f1a7e9e
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 69 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ project are *intended* to be supported indefinitely once the format is
declared stable. Any breaking changes to a format would be provided by
releasing a new format version with those changes.

> [!WARNING]
>
> `format0` is an unstable/development format version used for testing changes
> in behavior prior to *potentially* including them in a later stable format
> version. You are encouraged to use a stable format version (e.g., `1`)
> instead of using this version.
Top-level library constants are provided which identity the oldest and newest
stable format versions along with separate constants which identify the oldest
and newest format versions regardless of stability expectations. See those
constants for more information.

## Contributions

This library has a very narrow focus. While PRs may be accepted to resolve
Expand Down
16 changes: 6 additions & 10 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,14 @@ import (
"time"

payload "github.com/atc0005/cert-payload"
format0 "github.com/atc0005/cert-payload/format/v0"
format1 "github.com/atc0005/cert-payload/format/v1"
)

// Example of parsing a previously retrieved Nagios XI API response (saved to
// a JSON file) from the /nagiosxi/api/v1/objects/servicestatus endpoint,
// extracting and decoding an embedded certificate metadata payload from each
// status entry and then unmarshalling the result into a specific format
// version (in this case format 0).
//
// TODO: Update this example once format version 1 is released.
//
//gocognit:ignore
// version (in this case format 1).
func Example_extractandDecodePayloadsFromNagiosXIAPI() {
if len(os.Args) < 2 {
fmt.Println("Missing input file")
Expand Down Expand Up @@ -71,21 +67,21 @@ func Example_extractandDecodePayloadsFromNagiosXIAPI() {
continue // we have some known cases of explicitly excluding payload generation
}

format0Payload := format0.CertChainPayload{}
jsonDecodeErr := payload.Decode(unencodedPayload, &format0Payload)
format1Payload := format1.CertChainPayload{}
jsonDecodeErr := payload.Decode(unencodedPayload, &format1Payload)
if jsonDecodeErr != nil {
fmt.Println("Failed to decode JSON payload from original plugin output:", jsonDecodeErr)
os.Exit(1)
}

if !format0Payload.Issues.Confirmed() {
if !format1Payload.Issues.Confirmed() {
fmt.Print(" Skipping (no cert chain issues detected)")
continue
}

fmt.Printf(
"\nJSON payload for %s (flagged as problematic):\n",
format0Payload.Server,
format1Payload.Server,
)

var prettyJSON bytes.Buffer
Expand Down
10 changes: 7 additions & 3 deletions format/v0/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.

// Package format0 implements the initial certificate payload format.
// Package format0 implements the initial and ongoing development/unstable
// certificate payload format.
//
// NOTE: Until format v1 is released this format is subject to change
// frequently.
// NOTE: Even after format v1 is released this format is subject to change
// frequently. You are encouraged to use stable format versions 1 and higher
// instead of this format unless testing recent changes.
//
// NOTE: This format version provides no expectations of stability.
package format0
8 changes: 6 additions & 2 deletions format/v0/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ import (

const (
// FormatVersion indicates the format version support provided by this
// package. Version 0 is the pre-release version that we'll continue to
// use until the types in this package stabilize.
// package. Version 0 is the unstable format version that we'll continue
// to update over time as we test new functionality for potential
// inclusion in a later stable format version.
//
// NOTE: You are encouraged to use a stable format version (e.g., 1) if
// stability is a goal.
FormatVersion int = 0
)

Expand Down
6 changes: 4 additions & 2 deletions format/v1/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

// Package format1 implements the initial stable certificate payload format.
//
// FIXME: This is a mockup and not a real implementation. Please do not use
// this format version until this note has been removed.
// This and other stable format versions are subject to small compatible
// changes as needed to fix discovered issues and clarify behavior.
//
// Upgrade to the latest format version for new functionality.
package format1
8 changes: 6 additions & 2 deletions format/v1/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,17 @@ func Encode(inputData input.Values) ([]byte, error) {
certChainOriginal = nil
}

server := Server{
HostValue: inputData.Server.HostValue,
IPAddress: inputData.Server.IPAddress,
}

payload := CertChainPayload{
FormatVersion: FormatVersion,
Errors: shared.ErrorsToStrings(inputData.Errors),
TestingOutofTacos: false, // fake; force payload conflict with format version 0
CertChainOriginal: certChainOriginal,
CertChainSubset: certChainSubset,
Server: inputData.Server.HostValue,
Server: server,
DNSName: inputData.DNSName,
TCPPort: inputData.TCPPort,
Issues: certChainIssues,
Expand Down
35 changes: 17 additions & 18 deletions format/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.

// FIXME: This is a mockup and not a real implementation as format version 0
// is still being actively updated; format version 1 is intended to contrast
// with format version 0 for dev/testing purposes.

package format1

import (
Expand All @@ -17,15 +13,23 @@ import (

const (
// FormatVersion indicates the format version support provided by this
// package. Version 1 is the first stable release version that we'll
// support once the provided types & behavior stabilizes.
//
// FIXME: Format version 1 (at this time) is just a placeholder to help
// with initial testing.
//
// package.
FormatVersion int = 1
)

// Server reflects the host value and resolved IP Address used to retrieve the
// certificate chain.
type Server struct {
// HostValue is the original hostname value. While usually a FQDN, this
// value could also be a fixed IP Address (e.g., if SNI support wasn't
// used to retrieve the certificate chain).
HostValue string `json:"host_value"`

// IPAddress is the resolved IP Address for the hostname value used to
// retrieve a certificate chain.
IPAddress string `json:"ip_address"`
}

// CertificateStatus is the overall status of a certificate.
//
// - no problems (ok)
Expand Down Expand Up @@ -238,11 +242,6 @@ type CertChainPayload struct {
//
Errors []string `json:"errors"`

// TestingOutofTacos is a fake field used just to make sure that the
// "parent" type provided by this package differs from format version 0
// for testing purposes.
TestingOutofTacos bool

// CertChainOriginal is the original certificate chain entries encoded in
// PEM format.
//
Expand All @@ -254,9 +253,9 @@ type CertChainPayload struct {
// chain metadata. This field should always be populated.
CertChainSubset []Certificate `json:"cert_chain_subset"`

// Server is the FQDN or IP Address specified to the plugin which was used
// to retrieve the certificate chain.
Server string `json:"server"` // FIXME: Intentionally leaving this as a string instead of the Server type
// Server reflects the host value and resolved IP Address (which could be
// the same value) used to retrieve the certificate chain.
Server Server `json:"server"`

// A fully-qualified domain name or IP Address in the Subject Alternate
// Names (SANs) list for the leaf certificate.
Expand Down
88 changes: 56 additions & 32 deletions payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,36 @@ import (
"github.com/atc0005/cert-payload/input"
)

// Minimum and Maximum supported (stable) format versions. There may be format
// versions outside of these values but they are not considered
// stable/supported.
const (
// MaxSupportedPayloadVersion indicates the latest payload format version
// supported by this project. Update to the very latest project release to
// support the most recent format version.
//
// FIXME: Bump to `1` once the format stabilizes. Keep bumping version to
// reflect the most recent format version.
//
MaxSupportedPayloadVersion int = 1 // FIXME: Only for testing purposes.
// MaxStablePayloadVersion indicates the newest stable payload format
// version supported by this project. Update to the very latest project
// release to support the most recent stable format version.
MaxStablePayloadVersion int = 1

// MinStablePayloadVersion indicates the oldest stable payload format
// version supported by this project.
MinStablePayloadVersion int = 1
)

// MinSupportedPayloadVersion indicates the oldest payload format version
// supported by this project. Versions older than this are considered
// unstable and associated with early development releases and are no
// longer supported.
//
// FIXME: Bump to `1` once the format stabilizes.
//
MinSupportedPayloadVersion int = 0
// Minimum and Maximum format versions, regarding of stability expectations.
const (
// UnstablePayloadVersion is the development or unstable format version.
// Despite the low payload format version number this format is used for
// ongoing development purposes. No stability guarantees are provided.
UnstablePayloadVersion int = 0

// MaxPayloadVersion indicates the latest payload format version provided
// by this project. This value does not necessarily indicate the latest
// stable version. Update to the very latest project release to support
// the most recent format version.
MaxPayloadVersion int = MaxStablePayloadVersion

// MinPayloadVersion indicates the minimum payload format version
// supported by this project.
MinPayloadVersion int = UnstablePayloadVersion
)

var (
Expand All @@ -48,7 +60,7 @@ var (

// ErrPayloadFormatVersionTooOld indicates that a specified payload format
// version is no longer supported.
ErrPayloadFormatVersionTooOld = errors.New("requested payload format version is no longer supported")
// ErrPayloadFormatVersionTooOld = errors.New("requested payload format version is no longer supported")

// ErrPayloadFormatVersionTooNew indicates that a specified payload format
// version is not supported by this package release version.
Expand All @@ -66,17 +78,19 @@ type minimumFormat struct {
// processing or if an invalid payload version format is specified.
func Encode(payloadVersion int, inputData input.Values) ([]byte, error) {
switch {
case payloadVersion < MinSupportedPayloadVersion:
return nil, fmt.Errorf("payload version %d specified (min supported is %d): %w",
case payloadVersion < MinPayloadVersion:
return nil, fmt.Errorf("payload version %d specified (min stable is %d, min possible is %d): %w",
payloadVersion,
MinSupportedPayloadVersion,
ErrPayloadFormatVersionTooOld,
MinStablePayloadVersion,
MinPayloadVersion,
ErrUnsupportedPayloadFormatVersion,
)

case payloadVersion > MaxSupportedPayloadVersion:
return nil, fmt.Errorf("payload version %d specified (max supported is %d): %w",
case payloadVersion > MaxPayloadVersion:
return nil, fmt.Errorf("payload version %d specified (max stable is %d, max possible is %d): %w",
payloadVersion,
MaxSupportedPayloadVersion,
MaxStablePayloadVersion,
MaxPayloadVersion,
ErrPayloadFormatVersionTooNew,
)

Expand Down Expand Up @@ -119,13 +133,13 @@ func Decode(inputPayload string, dest interface{}) error {
}

switch {
case format.Version < MinSupportedPayloadVersion:
case format.Version < MinPayloadVersion:
return fmt.Errorf("payload version %d specified: %w",
format.Version,
ErrPayloadFormatVersionTooOld,
ErrUnsupportedPayloadFormatVersion,
)

case format.Version > MaxSupportedPayloadVersion:
case format.Version > MaxPayloadVersion:
return fmt.Errorf("payload version %d specified: %w",
format.Version,
ErrPayloadFormatVersionTooNew,
Expand Down Expand Up @@ -154,12 +168,22 @@ func Decode(inputPayload string, dest interface{}) error {
// metadata payloads.
func AvailableFormatVersions() []int {
return []int{
0,
1, // FIXME: Fake value for testing (for now)
2, // FIXME: Fake value for testing
3, // FIXME: Fake value for testing
4, // FIXME: Fake value for testing
UnstablePayloadVersion,
MaxStablePayloadVersion,
}
}

// AvailableStableFormatVersions provides a list of all available stable
// format versions that client applications may choose from when encoding or
// decoding certificate metadata payloads.
func AvailableStableFormatVersions() []int {
stableFormats := make([]int, 0, len(AvailableFormatVersions()))

for i := MinStablePayloadVersion; i <= MaxStablePayloadVersion; i++ {
stableFormats = append(stableFormats, i)
}

return stableFormats
}

// latestVersionEncoder is a helper function that provides the latest format
Expand Down

0 comments on commit f1a7e9e

Please sign in to comment.