I'm having some trouble with pulling the state of my custom resources through a reflector.
let ns = env::var("NAMESPACE").expect("Need NAMESPACE evar");
let fancy_cr: Api<FancyStruct> = Api::namespaced(client.clone(), &ns);
let (fancy_store, writer) = reflector::store::<FancyStruct>();
let lp = ListParams::default().timeout(20);
// I do see resources' name properly printed
for r in fancy_cr.list(&ListParams::default().timeout(20)).await? {
println!("Found resource: {}", r.name());
}
let rf = reflector(writer, watcher(manifests_cr, lp));
# Will print: "State: []" -> This is empty
println!("State: {:?}", fancy_store.state());
With the snippet above, I can list my custom resources (I can print their name). However calling state() doesn't return anything, so I can't continuously watch the state of my resources.
These are the versions of the dependencies I use (the latest versions as of today):
kube = { version = "0.76.0", features=["rustls-tls", "runtime"], default-features = false}
k8s-openapi = { version = "0.16.0", features = ["v1_25"] }
Would you mind helping me figuring that out please?
Related
I'm trying to write a Udp Client Actor using Actix. I've followed this example, UDP-Echo, but I can't seem to send a message to the server using the UdpFramed tokio struct.
Here's what I have so far, this is the Udp Client Actor implementation
use std::collections::HashMap;
use std::net::{SocketAddr};
use actix_rt::net::UdpSocket;
use actix::{Actor, Addr, AsyncContext, Context, Handler, StreamHandler, Message};
use actix::io::SinkWrite;
use actix_web::web::{Bytes, BytesMut};
use futures_util::stream::{SplitSink};
use futures_util::StreamExt;
use log::info;
use serde_json::Value;
use tokio_util::codec::BytesCodec;
use tokio_util::udp::UdpFramed;
use crate::rosclient::messages::Subscribe;
use std::io::Result;
mod messages;
type SinkItem = (Bytes, SocketAddr);
type UdpSink = SplitSink<UdpFramed<BytesCodec, UdpSocket>, SinkItem>;
pub struct UdpClientActor {
pub address: SocketAddr,
pub sink: SinkWrite<SinkItem, UdpSink>,
}
impl UdpClientActor {
pub fn start(udp: UdpSocket, address: SocketAddr) -> Addr<UdpClientActor> {
let framed = UdpFramed::new(udp, BytesCodec::new());
let (split_sink, split_stream) = framed.split();
UdpClientActor::create(|ctx| {
ctx.add_stream(split_stream.filter_map(
|item: Result<(BytesMut, SocketAddr)>| async {
item.map(|(data, sender)| UdpPacket(data, sender)).ok()
},
));
UdpClientActor {
address,
sink: SinkWrite::new(split_sink, ctx),
}
})
}
}
impl Actor for UdpClientActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
let mut hashmap = HashMap::new();
hashmap.insert(String::from("topic"), Value::String(String::from("/client_count")));
let subscription = Subscribe {
id: Default::default(),
op: "subscribe".to_string(),
extra: hashmap
};
ctx.notify(subscription);
}
}
#[derive(Message)]
#[rtype(result = "()")]
struct UdpPacket(BytesMut, SocketAddr);
impl StreamHandler<UdpPacket> for
UdpClientActor {
fn handle(&mut self, item: UdpPacket, _ctx: &mut Self::Context) {
println!("Received: ({:?}, {:?})", item.0, item.1);
self.sink.write((item.0.into(), item.1)).unwrap();
}
}
impl actix::io::WriteHandler<std::io::Error> for UdpClientActor {}
impl Handler<Subscribe> for UdpClientActor {
type Result = ();
fn handle(&mut self, msg: Subscribe, _ctx: &mut Self::Context) -> Self::Result {
let js = serde_json::json!(msg).to_string();
let _ = self.sink.write((Bytes::from(msg.to_string()), self.address));
info!("Subscribing to topic {}", js);
}
}
My main function creates the udp socket and spawns the actor.
fn main() {
////////////////////////////////////////////////////////////////////////////
let fut = async {
////////////////////////////////////////////////////////////////////////////
/////////// UDP_ACTOR
let sock = tokio::net::UdpSocket::bind("0.0.0.0:9091").await.unwrap();
let remote_addr = "172.30.89.169:9091".parse::<SocketAddr>().unwrap();
// let message = b"{ \"op\": \"subscribe\", \"topic\": \"/client_count\"}";
let _ = sock.connect(remote_addr).await;
// sock.send(message).await.unwrap();
let _udp_client = UdpClientActor::start(sock, remote_addr);
};
actix_rt::Arbiter::new().spawn(fut);
// system.block_on(fut);
system.run().unwrap();
}
If I remove the comments on
let message = b"{ \"op\": \"subscribe\", \"topic\": \"/client_count\"}";
and
sock.send(message).await.unwrap();
I can at least check that the server can actually receive messages. So I know that the problem must lie in my implementation of the actor. I do have another one which uses the LinesCodec instead of the BytesCodec, which follows the exact same implementation. Only difference is that SinkWrite becomes this:
SinkWrite<(String, SocketAddr), SplitSink<UdpFramed<codec::LinesCodec>,
(String, SocketAddr)>>
Here is my Cargo.toml for reference.
[package]
name = "local_websocket_client"
version = "0.1.0"
edition = "2018"
[dependencies]
actix="0.12"
actix-codec = "0.4"
actix-rt = "2.5"
bytestring = "1.0"
serde = {version="1.0", features=["serde_derive"]}
log = "0.4"
env_logger = "0.9.0"
chrono = "0.4"
dashmap = "4.0"
futures = "0.3"
openssl = "0.10"
tokio = { version = "1", features = ["full"] }
actix-web = "4.0.0-beta.15"
futures-util = "0.3"
tokio-util = { version="0.6", features=["net", "codec"] }
tokio-udp = "0.1.6"
bytes= { version="0.6", features=["serde"] }
[dependencies.awc]
features = ["openssl"]
version = "3.0.0-beta.9"
[dependencies.serde_json]
features = ["default"]
version = "1.0"
[dependencies.uuid]
features = ["v4", "serde", "v5"]
version = "0.8"
There are some extra crates there because I'm running 2 other websocket clients on the same application.
I would really appreciate some help on this matter.
Thank you
Solved it by wrapping the UdpSocket in an Arc and keeping the reference in the actor for later use. Using the socket to write messages works. The split stream used for the streamhandler needs no change, as it works as expected.
[MacOS 10.14.1, Xcode 10.1, Swift 4.2]
I'm working on creating a getopt style CLI argument processor whilst practising Swift. In my design, I decided to create a computed variable, represented as a [String:Bool] dictionary, that can be checked to see if an option (key) is just a switch (value = true) or whether it may include parameters (value = false). So I've written the code below, all of which is, at the moment, in my small (300 lines) main.swift file.
The code works correctly in a playground, but in my Swift Xcode project, whilst the dictionary's keys are correct, values are always false and inconsistent with the printed messages.
let options = "cwt:i:o:"
//lazy var optionIsSwitch : [String:Bool] = { (This will be moved to a class)
var optionIsSwitch : [String:Bool] = {
var tmpOptionIsSwitch : [String:Bool] = [:]
let optionsStrAsArray = Array(options)
let flags = Array(options.filter { !":".contains($0) } )
tmpOptionIsSwitch.reserveCapacity(flags.count)
for thisOption in 0...flags.count-1 {
var posInOptionsStr = 0
while posInOptionsStr < optionsStrAsArray.count-1 && flags[thisOption] != optionsStrAsArray[posInOptionsStr] {
posInOptionsStr += 1
}
if posInOptionsStr < optionsStrAsArray.count-1 && optionsStrAsArray[posInOptionsStr+1] == ":" {
tmpOptionIsSwitch[String(flags[thisOption])] = false
print("\(flags[thisOption]) is FALSE")
} else {
tmpOptionIsSwitch[String(flags[thisOption])] = true
print("\(flags[thisOption]) is TRUE")
}
}
return tmpOptionIsSwitch
}()
I've stepped through the code in my project to observe the execution sequence, and found it to be correct. As per the first image, tmpOptionIsSwitch returns a dictionary containing the right keys but all the values are set to false, which is inconsistent with the print statements.
As part of my debugging activities, I copied the above code into a Swift Playground where I found it gave the correct results, as per the image below.
Has anyone has such an issue? Is there something I've done wrong?
I'm having trouble with Realm giving me the error that a property of a given name does not exist for my object. But I know it does exist.
I've tried to follow the docs at https://realm.io/docs/swift/latest/#updating-values. I've searched for everything I can think of to find an applicable solution here and elsewhere, but nothing I've found works.
I previously performed a simple migration to just add properties to a different object within the same Realm. I just left the migration block empty and that worked fine. That migration should have set my schema from 0 to 1. If I run this with my schema set to 1 and to check for versions less than 2, it tells me that migration must be run in order to add these properties. The migration is running. I have a print statement for every time it executes. If I set the schema to 2, still checking for less than 2, I get the error for invalid property name for the old properties unless I uncomment them. Then I still get the error for the new properties.
Here's my Realm object. The commented out lines are the old properties I want to migrate from. The Int values are what I'm migrating to.
#objcMembers class Options: Object {
// dynamic var morningStartTime: Date?
// dynamic var afternoonStartTime: Date?
// dynamic var eveningStartTime: Date?
// dynamic var nightStartTime: Date?
dynamic var morningHour: Int = 7
dynamic var morningMinute: Int = 0
dynamic var afternoonHour: Int = 12
dynamic var afternoonMinute: Int = 0
dynamic var eveningHour: Int = 17
dynamic var eveningMinute: Int = 0
dynamic var nightHour: Int = 21
dynamic var nightMinute: Int = 0
dynamic var morningNotificationsOn: Bool = true
dynamic var afternoonNotificationsOn: Bool = true
dynamic var eveningNotificationsOn: Bool = true
dynamic var nightNotificationsOn: Bool = true
dynamic var firstItemAdded: Bool = false
dynamic var smartSnooze: Bool = false
dynamic var optionsKey = UUID().uuidString
override static func primaryKey() -> String? {
return "optionsKey"
}
}
My migration block:
let config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
schemaVersion: 2,
// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { migration, oldSchemaVersion in
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 2) {
migration.enumerateObjects(ofType: Options.className(), { (newObject, oldObject) in
let morningStartTime = oldObject!["morningStartTime"] as! Date?
let afternoonStartTime = oldObject!["afternoonStartTime"] as! Date?
let eveningStartTime = oldObject!["eveningStartTime"] as! Date?
let nightStartTime = oldObject!["nightStartTime"] as! Date?
newObject!["morningHour"] = self.getHour(date: morningStartTime)
newObject!["morningMinute"] = self.getMinute(date: morningStartTime)
newObject!["afternoonHour"] = self.getHour(date: afternoonStartTime)
newObject!["afternoonMinute"] = self.getMinute(date: afternoonStartTime)
newObject!["eveningHour"] = self.getHour(date: eveningStartTime)
newObject!["eveningMinute"] = self.getMinute(date: eveningStartTime)
newObject!["nightHour"] = self.getHour(date: nightStartTime)
newObject!["nightMinute"] = self.getMinute(date: nightStartTime)
})
}
})
getHour and getMinute are just functions I wrote to return an Int for the hour or minute from a Date. In case it's relevant, here they are.
func getHour(date: Date?) -> Int {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH"
let hour = dateFormatter.string(from: date!)
return Int(hour)!
}
func getMinute(date: Date?) -> Int {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "mm"
let minutes = dateFormatter.string(from: date!)
return Int(minutes)!
}
I know this is not the way to do it, but I made it work by taking a slightly more manual approach to the migration block. I uncommented the old properties in the Options object and changed my migration function to the following:
func migrateRealm() {
let configCheck = Realm.Configuration();
do {
let fileUrlIs = try schemaVersionAtURL(configCheck.fileURL!)
print("schema version \(fileUrlIs)")
} catch {
print(error)
}
print("performing realm migration")
let config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
schemaVersion: 2,
// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { migration, oldSchemaVersion in
print("oldSchemaVersion: \(oldSchemaVersion)")
if (oldSchemaVersion < 2) {
print("Migration block running")
DispatchQueue(label: self.realmDispatchQueueLabel).async {
autoreleasepool {
let realm = try! Realm()
let options = realm.object(ofType: Options.self, forPrimaryKey: self.optionsKey)
do {
try realm.write {
if let morningTime = options?.morningStartTime {
options?.morningHour = self.getHour(date: morningTime)
options?.morningMinute = self.getMinute(date: morningTime)
}
if let afternoonTime = options?.afternoonStartTime {
options?.afternoonHour = self.getHour(date: afternoonTime)
options?.afternoonMinute = self.getMinute(date: afternoonTime)
}
if let eveningTime = options?.eveningStartTime {
options?.eveningHour = self.getHour(date: eveningTime)
options?.eveningMinute = self.getMinute(date: eveningTime)
}
if let nightTime = options?.nightStartTime {
options?.nightHour = self.getHour(date: nightTime)
options?.nightMinute = self.getMinute(date: nightTime)
}
}
} catch {
print("Error with migration")
}
}
}
}
})
// Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config
// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
_ = try! Realm()
}
This only worked if I queued it on another thread asynchronously. I imagine if this data had been necessary for my initial view controller, then it probably would have created a race condition that caused the app to crash. Luckily this only appears in a secondary view, so it had time to complete before these values are needed. I guess I'll have to remove the unused properties with an updated Realm schema in a future version of my app.
Recently started working on XCode UI test with SWIFT.
My problem is I need to wait until a element appears on iPhone screen.
I found a solution with '''expectationForPredicate''' and '''waitForExpectationsWithTimeout''' but the problem this is this methods are designed to fail test case if expected predicate not matched within timeout.
I need a code which can wait for element to appear on screen if the element did not appear and timeout exceeded then I don't want test case to fail. rather I would like to return true (element exists) / false (not exists)
I found a solution by avoiding the above mentioned functions as those are failing my tests instead of returning true or false
Below is the method i created
func waitForElementToAppear(element: XCUIElement, file: String = #file, line: UInt = #line) -> Bool {
let TIMEOUT: Double = 120 ;
var isFound = false;
let start = NSDate();
var diff : Double = 0;
repeat{
print("Is element \(element) found : \(element.exists)")
print("Printing debugDescription -> ")
print(XCUIApplication().debugDescription)
if(element.exists){
isFound = true;
break;
}
print("Waiting for element to exists... Time counter :\(diff)")
sleep(1)
let end = NSDate();
diff = end.timeIntervalSinceDate(start);
}while(diff <= TIMEOUT);
return isFound;
}
I hope this will help others, But if you still have any other better solution please answer here.
I've got my own Core Data function, which fetches data. This func is identical to my previous one, except entity names. Previous works great, but this fails with error EXC_BAD_ACCESS when I try to get data from fetchedData .
func fetchGroups() -> Array<Group> {
var groups: Array<Group> = []
let fetchRequest: NSFetchRequest<Public> = Public.fetchRequest()
fetchRequest.sortDescriptors = [SortDescriptor.init(key: "publicTitle", ascending: true)]
let fetchedData = try! context.fetch(fetchRequest)
if (!fetchedData.isEmpty) {
for i in 0...fetchedData.count-1 {
print(fetchedData[0])
var group: Group = Group()
group.groupName = fetchedData[i].publicTitle
group.groupPhoto = fetchedData[i].publicPhoto
group.groupID = Int(fetchedData[i].publicID)
groups.append(group)
}
return groups
}
else {
return groups
}
}
So if it executes the code in brackets after if (!fetchedData.isEmpty), array isn't empty. Why it fails on getting elements?
P.S. fetchedData.count = 1; But fetchedData[0] = BIG CRASH! Magic.
After a long research I found that the problem was too little but very hard to find. I just checked my coredata.xcdatamodeld file and noticed, that Public entity doesn't have Class definition. By default all entities have only names.