blob: 104f7258101c52659b423e2d3069220daf37ce17 [file]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package api
import (
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"infra/cros/lab_inventory/utils"
)
func checkDuplicatedString() (input chan string, result chan bool) {
input = make(chan string)
result = make(chan bool)
set := map[string]bool{}
go func() {
defer close(result)
for i := range input {
_, existing := set[i]
set[i] = true
result <- existing
}
}()
return
}
// Validate validates input requests and return error if it's not.
//
// All devices should have unique hostname and/or id.
// Doesn't allow mix of DUT and labstation in RPC request. They should be
// deployed separatedly.
func (r *AddCrosDevicesRequest) Validate() error {
if r.Devices == nil || len(r.Devices) == 0 {
return status.Errorf(codes.InvalidArgument, "no devices to add")
}
hostnameChecker, duplicatedHostname := checkDuplicatedString()
defer close(hostnameChecker)
idChecker, duplicatedID := checkDuplicatedString()
defer close(idChecker)
hasDut, hasLabstation := false, false
for _, d := range r.Devices {
// Hostname is required.
hostname := utils.GetHostname(d)
if hostname == "" {
return status.Errorf(codes.InvalidArgument, "Hostname is missing")
}
// Hostname must be unique.
if hostnameChecker <- hostname; <-duplicatedHostname {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated hostname found: %s", hostname))
}
// ID is optional, but it must be unique if presents.
id := d.GetId().GetValue()
if id != "" {
if idChecker <- id; <-duplicatedID {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated id found: %s", id))
}
}
switch {
case d.GetDut() != nil:
hasDut = true
case d.GetLabstation() != nil:
hasLabstation = true
}
if hasDut && hasLabstation {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("DUT and labstation mixed in one request"))
}
}
return nil
}
// Validate validates input requests and return error if it's not.
func (r *DeleteCrosDevicesRequest) Validate() error {
if r.Ids == nil || len(r.Ids) == 0 {
return status.Errorf(codes.InvalidArgument, "no devices to remove")
}
hostnameChecker, duplicatedHostname := checkDuplicatedString()
defer close(hostnameChecker)
idChecker, duplicatedID := checkDuplicatedString()
defer close(idChecker)
for _, id := range r.Ids {
if _, ok := id.GetId().(*DeviceID_Hostname); ok {
hostname := id.GetHostname()
if hostnameChecker <- hostname; <-duplicatedHostname {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated hostname found: %s", hostname))
}
} else {
devID := id.GetChromeosDeviceId()
if idChecker <- devID; <-duplicatedID {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated id found: %s", devID))
}
}
}
return nil
}
// Validate validates getting requests must be non-empty and return error if
// it's not.
func (r *GetCrosDevicesRequest) Validate() error {
if len(r.GetIds()) == 0 && len(r.GetModels()) == 0 {
return status.Errorf(codes.InvalidArgument, "must specify device ID(s) to get")
}
return nil
}
// Validate validates input requests and return error if it's not.
func (r *UpdateLabstationsRequest) Validate() error {
if r.GetHostname() == "" {
return status.Errorf(codes.InvalidArgument, "Empty labstation hostname")
}
return nil
}
// Validate validates input requests and return error if it's not.
func (r *UpdateCrosDevicesSetupRequest) Validate() error {
if r.Devices == nil || len(r.Devices) == 0 {
return status.Errorf(codes.InvalidArgument, "no devices to update")
}
// There must be no dupicated ID in the request.
idChecker, duplicatedID := checkDuplicatedString()
defer close(idChecker)
for _, d := range r.Devices {
id := d.GetId().GetValue()
if idChecker <- id; <-duplicatedID {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated id found: %s", id))
}
}
return nil
}
// Validate validates input requests and return error if it's not.
func (r *UpdateDutsStatusRequest) Validate() error {
if r.States == nil || len(r.States) == 0 {
return status.Errorf(codes.InvalidArgument, "no devices to update")
}
// There must be no dupicated ID in the request.
idChecker, duplicatedID := checkDuplicatedString()
defer close(idChecker)
idWithStates := make(map[string]bool, len(r.States))
for _, d := range r.States {
id := d.GetId().GetValue()
idWithStates[id] = true
if idChecker <- id; <-duplicatedID {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated id found: %s", id))
}
}
idChecker2, duplicatedID2 := checkDuplicatedString()
defer close(idChecker2)
for _, d := range r.GetDutMetas() {
id := d.GetChromeosDeviceId()
if idChecker2 <- id; <-duplicatedID2 {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated id found in meta : %s", id))
}
}
for _, d := range r.GetDutMetas() {
id := d.GetChromeosDeviceId()
if _, ok := idWithStates[id]; !ok {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Cannot update meta without valid dut states: %s", id))
}
}
return nil
}
// Validate validates input requests and return error if it's not.
func (r *BatchUpdateDevicesRequest) Validate() error {
if len(r.GetDeviceProperties()) == 0 {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("no devices to update"))
}
hostnameChecker, duplicatedHostname := checkDuplicatedString()
defer close(hostnameChecker)
for _, p := range r.GetDeviceProperties() {
// Hostname is required.
hostname := p.GetHostname()
if hostname == "" {
return status.Errorf(codes.InvalidArgument, "Hostname is missing")
}
// Hostname must be unique.
if hostnameChecker <- hostname; <-duplicatedHostname {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("Duplicated hostname found: %s", hostname))
}
if p.GetPool() != "" {
continue
}
if rpm := p.GetRpm(); rpm == nil || (rpm.GetPowerunitName() == "" && rpm.GetPowerunitOutlet() == "") {
return status.Errorf(codes.InvalidArgument, fmt.Sprintf("nothing to update for %s", hostname))
}
}
return nil
}
// Validate validates input requests of AddAssets and UpdateAssets.
func (r *AssetList) Validate() error {
if r.Asset == nil || len(r.Asset) == 0 {
return status.Errorf(codes.InvalidArgument, "no asset to add/update")
}
return nil
}
// Validate validates input requests of GetManufacturingConfig.
func (r *GetManufacturingConfigRequest) Validate() error {
if r == nil || r.Name == "" {
return status.Errorf(codes.InvalidArgument, "Request is empty")
}
return nil
}
// Validate validates input requests of GetDeviceConfig.
func (r *GetDeviceConfigRequest) Validate() error {
if r == nil || r.GetConfigId() == nil {
return status.Errorf(codes.InvalidArgument, "Request is empty")
}
return nil
}
// Validate validates input requests of GetHwidData.
func (r *GetHwidDataRequest) Validate() error {
if r == nil || r.Name == "" {
return status.Errorf(codes.InvalidArgument, "Request is empty")
}
return nil
}