Skip to content

Latest commit

 

History

History
82 lines (63 loc) · 3.59 KB

File metadata and controls

82 lines (63 loc) · 3.59 KB

Maintainers Guide

This guide is intended for maintainers.

Release Branches

All development targets the main branch. New releases for the latest major version series are cut from this branch.

For the older major versions, we also have release-<major>.x branches where we try to backport all bug fixes.

Backports are done by tibdex/backport. Apply the label backport release-<major>.x to the PRs and once they are merged a new PR is opened.

Coding guidelines

Usage of pointers

  • Generally, pointers are used whenever a variable's value is nullable.
  • As few pointers as possible should be used to avoid nil dereference errors.
    • Leverage Go empty values where possible, e.g. if a value is required, so an empty value has no other meaning.
    • Sometimes the API treats an empty value as nil, for example an empty string in name updates. There no pointer should be used.
type ExampleUpdateRequest struct {
    // Name is optional but cannot be empty.
    // It doesn't need to be a pointer, we can represent empty values with "".
    Name string `json:"name,omitempty"`
    // Description is optional and can be empty.
    // We need a pointer to distinguish between an empty value and no value.
    Description *string `json:"description,omitempty"`
    // Foo is required, it should not be a pointer and should not be omitted.
    Foo int `json:"foo"`
    // Bar is required but nullable. It should be a pointer.
    Bar *int `json:"bar,omitempty"`
}

The schema package

  • We use the schema package to map JSON schemas from the API to Go structs as closely as possible.
  • Structs that fulfill some kind of function should be implemented in the hcloud package.
    • Structs can be converted to/from schemas using code generated by the goverter tool. (See schema_gen.go)
    • Conversion between structs should be possible without loss of information.

Warning

Since for maps and slices nil will serialize to {} or [] respectively, in the schema package optional maps/slices should be a pointer. In the hcloud package they should not be, because nil maps/slices can be mapped to a nil pointer during conversion.

Action endpoints

  • If an action endpoint returns more than just an action, a <...>Result struct should be used.
    • This struct must not be returned as a pointer.
    • If an error occurs, an empty struct should be returned.
  • Alternatively, only the action can be returned.
    • This should be done only if it is certain that in the future no additional values will be returned, as this would be a breaking change.

Error validation

  • We only do basic validation on the client side. Business logic is validated on the API side.
  • error.go contains helpers for validation. Validation should be implemented as a Validate() function on the Opts struct.
// Validate checks if options are valid.
func (o ExampleOpts) Validate() error {
    if o.Foo == "" {
        return missingField(o, "Foo")
    }
    if o.Bar == nil && o.Baz == nil {
        return missingOneOfFields(o, "Bar", "Baz")
    }
    if o.Qux <= 0 {
        return invalidFieldValue(o, "Qux", o.Qux)
    }
    return nil
}

Subresources

  • A subresource should contain a reference to its parent that can be used to address endpoints. This reduces duplication.
  • Example:
    • Correct: func UpdateSubaccount(context.Context, *hcloud.StorageBoxSubaccount, StorageBoxSubaccountUpdateOpts)
    • Wrong: func UpdateSubaccount(context.Context, *hcloud.StorageBox, *hcloud.StorageBoxSubaccount, StorageBoxSubaccountUpdateOpts)