github.com/zorawar87/trillian@v1.2.1/quota/etcd/quotaapi/conversions.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package quotaapi
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/google/trillian/quota/etcd/quotapb"
    21  	"github.com/google/trillian/quota/etcd/storagepb"
    22  	"google.golang.org/genproto/protobuf/field_mask"
    23  )
    24  
    25  const (
    26  	statePath           = "state"
    27  	maxTokensPath       = "max_tokens"
    28  	sequencingBasedPath = "sequencing_based"
    29  	timeBasedPath       = "time_based"
    30  )
    31  
    32  var (
    33  	commonMask          = &field_mask.FieldMask{Paths: []string{statePath, maxTokensPath}}
    34  	sequencingBasedMask = &field_mask.FieldMask{Paths: append(commonMask.Paths, sequencingBasedPath)}
    35  	timeBasedMask       = &field_mask.FieldMask{Paths: append(commonMask.Paths, timeBasedPath)}
    36  
    37  	errBothReplenishmentStrategies = fmt.Errorf("both %q and %q paths specified, please pick a single replenishment strategy", sequencingBasedPath, timeBasedPath)
    38  )
    39  
    40  // validateMask returns nil if mask is a valid Config mask, error otherwise.
    41  // Paths must match the proto name of the fields (e.g., "time_based", not "TimeBased").
    42  // If fields belong to a oneof (such as "sequencing_based" and "time_based"), then only one field of
    43  // the oneof may be specified.
    44  func validateMask(mask *field_mask.FieldMask) error {
    45  	sequencingBasedFound := false
    46  	timeBasedFound := false
    47  	for _, path := range mask.Paths {
    48  		switch path {
    49  		case statePath, maxTokensPath:
    50  			// OK
    51  		case sequencingBasedPath:
    52  			if timeBasedFound {
    53  				return errBothReplenishmentStrategies
    54  			}
    55  			sequencingBasedFound = true
    56  		case timeBasedPath:
    57  			if sequencingBasedFound {
    58  				return errBothReplenishmentStrategies
    59  			}
    60  			timeBasedFound = true
    61  		default:
    62  			return fmt.Errorf("invalid field path for Config: %q", path)
    63  		}
    64  	}
    65  	return nil
    66  }
    67  
    68  // applyMask copies the fields specified by mask from src to dest. The mask must be first validated
    69  // by validateMask(), as unknown paths are simply ignored by applyMask.
    70  // Paths must match the proto name of the fields (e.g., "time_based", not "TimeBased").
    71  func applyMask(src *quotapb.Config, dest *storagepb.Config, mask *field_mask.FieldMask) {
    72  	for _, path := range mask.Paths {
    73  		switch path {
    74  		case statePath:
    75  			dest.State = storagepb.Config_State(storagepb.Config_State_value[src.State.String()])
    76  		case maxTokensPath:
    77  			dest.MaxTokens = src.MaxTokens
    78  		case sequencingBasedPath:
    79  			if src.GetSequencingBased() == nil {
    80  				dest.ReplenishmentStrategy = nil
    81  			} else {
    82  				dest.ReplenishmentStrategy = &storagepb.Config_SequencingBased{
    83  					SequencingBased: &storagepb.SequencingBasedStrategy{},
    84  				}
    85  			}
    86  		case timeBasedPath:
    87  			if tb := src.GetTimeBased(); tb == nil {
    88  				dest.ReplenishmentStrategy = nil
    89  			} else {
    90  				dest.ReplenishmentStrategy = &storagepb.Config_TimeBased{
    91  					TimeBased: &storagepb.TimeBasedStrategy{
    92  						TokensToReplenish:        tb.GetTokensToReplenish(),
    93  						ReplenishIntervalSeconds: tb.GetReplenishIntervalSeconds(),
    94  					},
    95  				}
    96  			}
    97  		}
    98  	}
    99  }
   100  
   101  // convertToAPI returns the API representation of a storagepb.Config proto.
   102  func convertToAPI(src *storagepb.Config) *quotapb.Config {
   103  	dest := &quotapb.Config{
   104  		Name:      src.Name,
   105  		State:     quotapb.Config_State(quotapb.Config_State_value[src.State.String()]),
   106  		MaxTokens: src.MaxTokens,
   107  	}
   108  	sb := src.GetSequencingBased()
   109  	tb := src.GetTimeBased()
   110  	switch {
   111  	case sb != nil:
   112  		dest.ReplenishmentStrategy = &quotapb.Config_SequencingBased{
   113  			SequencingBased: &quotapb.SequencingBasedStrategy{},
   114  		}
   115  	case tb != nil:
   116  		dest.ReplenishmentStrategy = &quotapb.Config_TimeBased{
   117  			TimeBased: &quotapb.TimeBasedStrategy{
   118  				TokensToReplenish:        tb.TokensToReplenish,
   119  				ReplenishIntervalSeconds: tb.ReplenishIntervalSeconds,
   120  			},
   121  		}
   122  	}
   123  	return dest
   124  }
   125  
   126  // convertToStorage returns the storage representation of a quotapb.Config proto.
   127  func convertToStorage(src *quotapb.Config) *storagepb.Config {
   128  	// Instead of potentially duplicating logic, let's take advantage of applyMask by picking a
   129  	// pre-made mask that contains all fields we care about and apply it to a new proto.
   130  	var mask *field_mask.FieldMask
   131  	switch {
   132  	case src.GetSequencingBased() != nil:
   133  		mask = sequencingBasedMask
   134  	case src.GetTimeBased() != nil:
   135  		mask = timeBasedMask
   136  	default:
   137  		mask = commonMask
   138  	}
   139  	dest := &storagepb.Config{Name: src.Name}
   140  	applyMask(src, dest, mask)
   141  	return dest
   142  }